@AliasFor是spring注解体系中一个非常重要且基础的注解。顾名思义,它的基本作用就是为注解字段定义一个别名。
基本作用:字段别名
java"> @Testpublic void test3AliasFor() {try {{CasbanScan casbanScan = AnnotationUtils.findAnnotation(UserSummy.class, CasbanScan.class);System.out.println(casbanScan);}} catch (Throwable e) {e.printStackTrace();fail();}}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface CasbanScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};}@CasbanScan("hello")public static class UserSummy{}public static class VipSummy extends UserSummy{}
输出
@CasbanScan(basePackages=[hello], value=[hello])
上面的输出可以看到虽然UserSummy
类上的@CasbanScan
注解只定义了value
字段但通过AnnotationUtils.findAnnotation
方法获取到的CasbanScan
实例basePackages
字段也是有与value
字段相同的值,这就是@AliasFor
注解的基本作用。
更多:注解继承
除了为注解字段定义别名之外,@AliasFor
注解更重要的作用是为注解增加了类似Java普通类的继承功能,如下是@AliasFor
的定义源码:
java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {/*** Alias for {@link #attribute}.* <p>Intended to be used instead of {@link #attribute} when {@link #annotation}* is not declared — for example: {@code @AliasFor("value")} instead of* {@code @AliasFor(attribute = "value")}.*/@AliasFor("attribute")String value() default "";/*** The name of the attribute that <em>this</em> attribute is an alias for.* @see #value*/@AliasFor("value")String attribute() default "";/*** The type of annotation in which the aliased {@link #attribute} is declared.* <p>Defaults to {@link Annotation}, implying that the aliased attribute is* declared in the same annotation as <em>this</em> attribute.*/Class<? extends Annotation> annotation() default Annotation.class;}
上面源码可以看到@AliasFor
不仅有value
字段还有annotation
字段,这个字段的字面作用是声明别名属性的所属的注解类型。
怎么理解呢?
我们知道注解(Annotation)类虽然也是一个接口(@interface
),但它与Java的标准接口(interface)是不太一样的,它没有继承能力,
如下普通的Java interface类可以从另一个接口类继承(extends),但@interface
不行
java">public interface Request{}
public interface WebRequest extends Request{}
这在一定程序上限制了注解的应用场景。
spring-core的注解体系通过@AliasFor
注解为注解设计提供了继承能力。这个继承能力就体现在了@AliasFor
的annotation
字段。
如果不定义annotation
字段,
@AliasFor
的value
或attribute
字段用于指定@AliasFor
所在字段为同当前注解类内的字段别名。如下例子:value
上定义的@AliasFor("basePackages")
即指定value
为basePackages
字段的别名,反之亦然。
java"> public @interface CasbanScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};}
如果定义了annotation
字段,
annotation
字段可以理解为指定当前字段为定义在当前注解上的注解类指定字段的别名。
如下示例:
@RequestComponent
注解上定义了@ScanConfig
注解,@RequestComponent
注解的subClassFirstly
字段上定义了@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")
即定义该字段为@ScanConfig
的subClassFirstly
的别名
java">@Retention(RetentionPolicy.RUNTIME)
public @interface ScanConfig {String subClassFirstly() default "";
}@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@ScanConfig
public @interface RequestComponent {@AliasFor("associated")String value() default "";@AliasFor("value")String associated() default "";@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")String subClassFirstly() default "";
}
我们可以再定义一个注解@WebComponent
也使定义一个firstly
字段使用同样的@AliasFor
注解,注意这个字段firstly
字段与@RequestComponent
的subClassFirstly
并不同名
java">@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@ScanConfig
public @interface WebComponent {String value() default "";@AliasFor(annotation = ScanConfig.class,attribute="subClassFirstly")String firstly() default "";
}
展现继承能力
如下示例,调用AnnotatedElementUtils.findMergedAnnotation
方法获取类上定义的@ScanConfig
java"> @Testpublic void test4AliasFor() {try {{ScanConfig scanConfig= AnnotatedElementUtils.findMergedAnnotation(UseRequestComponent.class, ScanConfig.class);System.out.printf("ScanConfig:%s in %s\n",UseRequestComponent.class.getSimpleName(), AnnotationUtils.getAnnotationAttributes(scanConfig));assertEquals("hello", scanConfig.subClassFirstly());}{ScanConfig scanConfig = AnnotatedElementUtils.findMergedAnnotation(UseWebComponent.class, ScanConfig.class);System.out.printf("ScanConfig:%s in %s\n",UseWebComponent.class.getSimpleName(), AnnotationUtils.getAnnotationAttributes(scanConfig));assertEquals("world", scanConfig.subClassFirstly());}} catch (Throwable e) {e.printStackTrace();fail();}}@RequestComponent(subClassFirstly="hello")public static class UseRequestComponent{}@WebComponent(firstly = "world")public static class UseWebComponent{}
输出
ScanConfig:UseRequestComponent in {subClassFirstly=hello}
ScanConfig:UseWebComponent in {subClassFirstly=world}
如上示例,我们可以从定义了注解@RequestComponent(subClassFirstly="hello")
的UseRequestComponent
类直接获取@ScanConfig
注解值
也可以从定义了注解@WebComponent(firstly = "world")
的UseWebComponent
类直接获取@ScanConfig
注解值
虽然@RequestComponent
和@WebComponent
是两个不同的注解类,但因为它们都有@ScanConfig
注解。所以AnnotatedElementUtils.findMergedAnnotation
方法可以从定义了这两个注解的类上读取它们共同定义的@ScanConfig
注解,
从这点来看是不是可以视为@RequestComponent
和@WebComponent
都继承了将@ScanConfig
注解呢?
所以@AliasFor
注解可以为不同的注解提供共同的字段,类似于父类字段。有了这个能力,基于spring-core注解框架设计自己的注解体系就提供了很大的灵活性和便利性。