Skip to content

Instantly share code, notes, and snippets.

@codeman688
Created June 29, 2019 02:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save codeman688/575ce10fb6b6de5fe69fe63dab4ebfcc to your computer and use it in GitHub Desktop.
Save codeman688/575ce10fb6b6de5fe69fe63dab4ebfcc to your computer and use it in GitHub Desktop.

Usage of @AliasFor

In Spring's many annotations, it is often found that many different attributes of different annotations play the same role, such as @RequestMapping's value attribute and path attribute. In order to keep their behavior consistent, such as the value of value and the value of path can not conflict, their values should always be equal. To handle these issues uniformly, Spring created the @AliasFor tag.

The @AliasFor tag has the following usage methods.

1, explicit use within the same annotation

For example, the usage in @RequestMapping:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

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

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

    //...
}

In the future, the functions of path and value are the same.

Another example is the value attribute and the locations attribute in the @ContextConfiguration annotation:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContextConfiguration {
    @AliasFor("locations")
    String[] value() default {};

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

The two attributes locations and value are aliased to each other, and their functions are the same.

And also you need to pay attention to:

  • Annotaions that are aliased to each other must appear in pairs

    Since locations is an alias for value, value must also be used instead of locations. So the annotations must appear in pairs.

  • Attributes marked by @AliasFor must have default values

Then if the definition of the alias is violated, an error will be reported during the use. Let's do a simple test:

@ContextConfiguration(value = "aa.xml", locations = "bb.xml")
public class AnnotationUtilsTest {
    @Test
    public void testAliasfor() {
        ContextConfiguration cc = AnnotationUtils.findAnnotation(getClass(),
                ContextConfiguration.class);
        System.out.println(
                StringUtils.arrayToCommaDelimitedString(cc.locations()));
        System.out.println(StringUtils.arrayToCommaDelimitedString(cc.value()));
    }
}

20190629095422

If you try to perform the above test, you will find that the program is reporting an error. value and locations are aliases of each other and cannot be set to different values.

Adjust the code slightly:

@MyAnnotation
@ContextConfiguration(value = "aa.xml", locations = "aa.xml")
public class AnnotationUtilsTest {

or

@MyAnnotation
@ContextConfiguration(value = "aa.xml")
public class AnnotationUtilsTest {

and run:

aa.xml
aa.xml

You will find that the program is now working properly.

2, explicit cover the attributes in the meta-annotation

Let's look at a piece of code:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AopConfig.class)
public class AopUtilsTest {

This code is a very familiar JavaTest-based Spring test code. If I have a preference now, I think it's too much trouble to write @ContextConfiguration(classes = AopConfig.class) every time. I want to write a little simpler, I can define a tag like this:

@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration
public @interface STC {

    @AliasFor(value = "classes", annotation = ContextConfiguration.class)
    Class<?>[] cs() default {};

}

Because the @ContextConfiguration annotation itself is defined as @Inherited, our STC annotation can be understood as inheriting the @ContextConfiguration annotation.

I think the classes property is too long, so I created a cs property. In order to make this property equivalent to the classes property in the @ContextConfiguration property, I used the @AliasFor tag. I set value (as an alias for which attribute) and annotation (as an annotation).

Use our STC:

@RunWith(SpringJUnit4ClassRunner.class)
@STC(cs = AopConfig.class)
public class AopUtilsTest {

    @Autowired
    private IEmployeeService service;

works fine.

This is the second usage of the @AliasFor tag, which shows the aliases for the attributes in the meta annotation. There are also some restrictions at this time, such as the default value of the attribute type must be the same. Of course, in this use case, @AliasFor can only alias the meta annotation of the current annotation.

3, implicitly declare an alias in an annotation

This usage is similar to the second one. We use the Spring official documentation directly:

 @ContextConfiguration
 public @interface MyTestConfig {

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

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] groovyScripts() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles() default {};
 }

As you can see, in the MyTestConfig annotation, the alias @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") is defined on value, groovyScripts, xmlFiles. So, in fact, in this annotation, value, groovyScripts and xmlFiles are also aliases to each other.

4, the delivery of the alias

@AliasFor annotation is to allow the transfer between aliases, simply understand that if A is an alias of B, and B is an alias of C, then A is an alias of C;

 @MyTestConfig
 public @interface GroovyOrXmlTestConfig {

    @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
    String[] groovy() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xml() default {};
 }

1, GroovyOrXmlTestConfig take @MyTestConfig (refer to the previous case) as a meta-annotation;

2, defines the groovy attribute, and as an alias for the groovyScripts attribute in MyTestConfig;

3, defines the xml attribute, and as an alias for the locations attribute in ContextConfiguration;

4, because the groovyScripts attribute of MyTestConfig is itself an alias for the locations attribute in ContextConfiguration; so the xml attribute and the groovy attribute are also aliases to each other;

Read more in https://github.com/codeman-springboot/Blogs/wiki

@logbasex
Copy link

logbasex commented Jun 2, 2020

Thank you. But I still don't know why spring needs both path and value if the function are the same?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment