Handling annotations with Spring AnnotationUtils

Annotations in Java let us, programmers, to transfer some of configuration from configuration files into Java classes. For example, in Spring, we can configure URL mapping directly inside the controllers thanks to @RequestMapping annotation. But it couldn't be possible without several utilitary classes, like AnnotationUtils, described here.

In this article we'll discover an enormous facility provided with AnnotationUtils class. First of all, we'll show all available methods. In the second part we'll see the places where these methods are implemented. At the end we'll pass to some of practical examples.

What is AnnotationUtils class in Spring ?

AnnotationUtils is a utilitary class written to handle miscellaneous annotation problematics. Composed mainly by public and static methods, it allows to check the annotations at class, method or field level. In additionally, AnnotationUtils doesn't satisfy about plain class analyzing. It does little bit more by looking for annotation at superclasses and interfaces too. Based on API reflection, AnnotationUtils uses 3 elements of java.lang.reflect to deal with annotations:
- Annotation: represents annotation.
- AnnotatedElement: represents annotated element.
- Method: provides informations about a method in some class or interface.

Now, let's take a look on available and the most important public methods in AnnotationUtils class:
- getAnnotation: 3 methods with this name exist. The first one takes in parameter Annotation's instance. The second one takes AnnotatedElement's instance. The third getAnnotation method takes in parameter Method object. All of them return annotation from a field, class or method.
- getRepeatableAnnotation: accessible for Method or AnnotatedElement passed in parameter, they are two methods with this name. They return the elements on which an annotation is present. For example, it can returns the methods annotated with @RequestMapping annotation.
- findAnnotation: looks for annotation in Method or Class object passed in parameter.
- isAnnotationDeclaredLocally: checks if annotation is declared locally in class and is not inherited.
- isAnnotationInherited: checks if annotation is inherited from another class (ie not declared locally).
- getAnnotationAttributes: gets attributes of given annotation.
- getValue: gets value of given annotation. Two methods with this name exist. The first returns global value of annotation. The second one the value of specific annotation's parameter.
- getDefaultValue: gets default value of given annotation or annotation's attribute.

Where AnnotationUtils methods are used ?

AnnotationUtils is used in a lot of Spring projects. We'll focus on projects linked with core and web development: web, web MVC, context and beans. This is a list of AnnotationUtils uses in these Spring projects:

  1. web MVC

    • AnnotationMethodHandlerAdapter, used until 3.1 Spring's version as main handler for annotated method, employed AnnotationUtils to check different annotations available for methods level, like: @RequestMapping, @ResponseStatus, @ResponseBody or @ModelAttribute.
    • The AnnotationMethodHandlerAdapter successor, RequestMappingHandlerMapping, works with AnnotationUtils to resolve @RequestMapping and construct RequestMappingInfo object which encapsulates the mapping configuration (variables, HTTP methods, accepted headers etc.).
    • RequestMappingHandlerAdapter is the third important class of web MVC project which uses AnnotationUtils. It invokes findAnnotation() method in 2 public singletons, both are instances of MethodFilter class. One represents @InitBinder annotation and the second @ModelAttribute.
  2. web

    • HandlerMethodInvoker uses AnnotationUtils to know what is the meanning of @ModelAttribute annotation, if the object must be validated with @Valid or if the @InitBinder method is present.
    • Another key class of this project, HandlerMethodResolver, calls AnnotationUtils method to determine the type of method (handler, binder or model-attribute oriented). It accomplishes it through 3 methods: isHandlerMethod, isInitBinderMethod and isModelAttributeMethod. Each takes in parameter the Method's instance.
  3. context

    • The utilitary class for parsing beans annotations, BeanAnnotationHelper, uses findAnnotation() method from AnnotationUtils to deal with classes annotated with @Bean. The helper uses it for example, to determine the bean's name.
    • One more class at this level, AnnotationAsyncExecutionInterceptor, employs AnnotationUtils to resolve an annotation. It invokes findAnnotation() method to resolve the name of method executed at runtime.
  4. beans

    • AnnotationUtils is used here to retrieve annotations for beans. We can find these uses in StaticListableBeanFactory or DefaultListableBeanFactory classes.

Using AnnotationUtils in practice

To better understand the working way of AnnotationUtils in Spring, let's make two annotations: the first one for methods and the second one for classes. After that we'll make two test classes and one playground class which will output the results of annotations analyzing done by AnnotationUtils. Two annotations are :

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ClassNameAnnotation {

  String className() default "Empty class name";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface StaticTextAnnotation {

  String text() default "Default text for static text annotation";
  String value() default "Default value";
}

The @Retention annotation is mandatory. Otherwise AnnotationUtils won't be able to detect these annotations. Note the presence of value() attribute in StaticTextAnnotation and absence of this attribute in ClassNameAnnotation. In the following paragraphs you can find our test classes :

@ClassNameAnnotation(className = "TestChildren")
public class TestParent {

  @StaticTextAnnotation(value= "Custom text value", text = "Test text")
  public String test(HttpServletRequest request) {
    return "test";
  }
}
public class TestChildren extends TestParent {
}

TestChildren class hasn't any annotation. We'll use that to test inheritance annotations checking.

public class Playground {
  public static void main(String[] args) {
    try {
      Method method = TestParent.class.getMethod("test", new Class[]{HttpServletRequest.class});
      Annotation staticTextAnnot = AnnotationUtils.findAnnotation(method, StaticTextAnnotation.class);
      System.out.println("@StaticTextAnnotation of method is: "+staticTextAnnot);
      System.out.println("@StaticTextAnnotation method value: "+AnnotationUtils.getValue(staticTextAnnot, "text"));
      System.out.println("@StaticTextAnnotation method default value: "+AnnotationUtils.getDefaultValue(staticTextAnnot, "text"));
      System.out.println("@StaticTextAnnotation value: "+AnnotationUtils.getValue(staticTextAnnot));

      // inheriting annotations tests
      Annotation classNameAnnotation = AnnotationUtils.findAnnotation(TestChildren.class, ClassNameAnnotation.class);
      System.out.println("@ClassNameAnnotation of TestChildren.class is: "+classNameAnnotation);
      System.out.println("@ClassNameAnnotation method value: "+AnnotationUtils.getValue(classNameAnnotation, "className"));
      System.out.println("@ClassNameAnnotation method default value: "+AnnotationUtils.getDefaultValue(classNameAnnotation, "className"));
      System.out.println("@ClassNameAnnotation value: "+AnnotationUtils.getValue(classNameAnnotation));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
And that is the reslult of Playground's main method:
@StaticTextAnnotation of method is: @com.waitingforcode.annotations.StaticTextAnnotation(text=Test text, value=Custom text value)
@StaticTextAnnotation method value: Test text
@StaticTextAnnotation method default value: Default text for static text annotation
@StaticTextAnnotation value: Custom text value
@ClassNameAnnotation of TestChildren.class is: @com.waitingforcode.annotations.ClassNameAnnotation(className=TestChildren)
@ClassNameAnnotation method value: TestChildren
@ClassNameAnnotation method default value: Empty class name
@ClassNameAnnotation value: null

As you can see, we can investigate a lot of annotations points. We can check the value of attribute "value()" or another one customized attribute. We can also check the default values of the attributes. Beside of it, AnnotationUtils makes also possible annotations manipulations in inherited architectures.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!