Spring Top

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> LEARN SPRING

1. Overview

In this tutorial, we'll learn about the @AliasFor annotation in Spring.

First, we'll see examples from within the framework where it's in use. Next, we'll look at a few customized examples.

2. The Annotation

@AliasFor is part of the framework since version 4.2. Several core Spring annotations have been updated to include this annotation now.

We can use it to decorate attributes either within a single annotation or in an annotation composed from a meta-annotation. Namely, a meta-annotation is an annotation that can be applied to another one.

In the same annotation, we use @AliasFor to declare aliases for attributes so that we can apply them interchangeably. Alternatively, we can use it in a composed annotation to override an attribute in its meta-annotation. In other words, when we decorate an attribute in a composed annotation with @AliasFor, it overrides the specified attribute in its meta-annotation.

Interestingly, many-core Spring annotations such as @Bean, @ComponentScan, @Scope, @RequestMapping, and @RestController now use @AliasFor to configure their internal attribute aliases.

Here's the definition of the annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";
    
    @AliasFor("value")
    String attribute() default "";

    Class<? extends Annotation> annotation() default Annotation.class;
}

Importantly, we can use this annotation implicitly as well as explicitly. Implicit usage is only restricted to aliases within an annotation. In comparison, explicit usage can also be made for an attribute in a meta-annotation.

We'll see this in detail with examples in the following sections.

3. Explicit Aliases Within an Annotation

Let's consider a core Spring annotation, @ComponentScan, to understand explicit aliases within a single annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};
...
}

As we can see, value is defined here explicitly as an alias for basePackages, and vice-versa. This means we can use them interchangeably.

So effectively, these two usages are similar:

@ComponentScan(basePackages = "com.baeldung.aliasfor")

@ComponentScan(value = "com.baeldung.aliasfor")

Furthermore, since the two attributes are also marked as default, let's write this more concisely:

@ComponentScan("com.baeldung.aliasfor")

Also, there're a few implementation requirements that Spring mandates for this scenario. First, the aliased attributes should declare the same default value. Additionally, they should have the same return type. If we violate any of these constraints, the framework throws an AnnotationConfigurationException.

4. Explicit Aliases for Attribute in Meta-Annotation

Next, let's see an example of a meta-annotation and create a composed annotation from it. Then, we'll see the explicit usage of aliases in the custom one.

First, let's consider the framework annotation RequestMapping as our meta-annotation:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    
    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};
    ...
}

Next, we'll create a composed annotation MyMapping from it:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
public @interface MyMapping {
    @AliasFor(annotation = RequestMapping.class, attribute = "method")
    RequestMethod[] action() default {};
}

As we can see, in @MyMapping, action is an explicit alias for the attribute method in @RequestMapping. That is, action in our composed annotation overrides the method in the meta-annotation.

Similar to aliases within an annotation, meta-annotation attribute aliases must also have the same return type. For example, RequestMethod[] in our case. Furthermore, the attribute annotation should reference the meta-annotation as in our usage of annotation = RequestMapping.class.

To demonstrate, let's add a controller class called MyMappingController. We'll decorate its method with our custom annotation.

Specifically, here we'll add only two attributes to @MyMapping, route, and action:

@Controller
public class MyMappingController {

    @MyMapping(action = RequestMethod.PATCH, route = "/test")
    public void mappingMethod() {}
    
}

Finally, to see how explicit aliases behave, let's add a simple test:

@Test
public void givenComposedAnnotation_whenExplicitAlias_thenMetaAnnotationAttributeOverridden() {
    for (Method method : controllerClass.getMethods()) {
        if (method.isAnnotationPresent(MyMapping.class)) {
            MyMapping annotation = AnnotationUtils.findAnnotation(method, MyMapping.class);
            RequestMapping metaAnnotation = 
              AnnotationUtils.findAnnotation(method, RequestMapping.class);

            assertEquals(RequestMethod.PATCH, annotation.action()[0]);

            assertEquals(0, metaAnnotation.method().length);
        }
    }
}

As we can see, our custom annotation's attribute action has overridden the meta-annotation @RequestMapping‘s attribute method.

5. Implicit Aliases Within an Annotation

To understand this, let's add a few more aliases within our @MyMapping:

@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] value() default {};

@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] mapping() default {};
    
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] route() default {};

In this situation, value, mapping, and route are explicit meta-annotation overrides for path in @RequestMapping. Therefore, they are also implicit aliases of each other. In other words, for @MyMapping, we can use these three attributes interchangeably.

To demonstrate this, we'll use the same controller as in the previous section. And here's another test:

@Test
public void givenComposedAnnotation_whenImplictAlias_thenAttributesEqual() {
    for (Method method : controllerClass.getMethods()) {
        if (method.isAnnotationPresent(MyMapping.class)) {
            MyMapping annotationOnBean = 
              AnnotationUtils.findAnnotation(method, MyMapping.class);

            assertEquals(annotationOnBean.mapping()[0], annotationOnBean.route()[0]);
            assertEquals(annotationOnBean.value()[0], annotationOnBean.route()[0]);
        }
    }
}

Notably, we did not define the attributes value and mapping in the annotation on our controller method. However, they still implicitly carry the same value as route.

6. Conclusion

In this tutorial, we learned about the @AliasFor annotation in the Spring Framework. In our examples, we looked at explicit as well as implicit usage scenarios.

As always, source code is available over on GitHub.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> THE COURSE
Generic footer banner
Comments are closed on this article!