Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

NPI – Spring Top – Temp – Non-Geo (Lightrun)

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

>> LEARN SPRING
NPI – Lightrun – Spring (partner)

We rely on other people’s code in our own work. Every day. It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production - debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky. It’s difficult to understand what talks to what and, specifically, which part of the underlying library is at fault.

Lightrun is a new kind of debugger.

It's one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics. No hotfixes, redeployments, or restarts required.

Learn more in this quick, 5-minute Lightrun tutorial:

>> The Essential List of Spring Boot Annotations and Their Use Cases

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
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!