1. @AliasFor
@AliasFor是一个注解,用于为注解属性声明别名。
代码如下:它有两个属性value和attribute @AliasFor注解注释了自身,并且value和attribute互为别名:
@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;
}
2. 作用
2.1 AliasFor可以定义一个注解中的两个属性互为别名
如:
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
boolean lazyInit() default false;
...
}
ComponentScan中的value和basePackages作用是一样的。
@ComponentScan("com.binecy")
public class SimpleAlias {
public static void main(String[] args) {
ComponentScan ann = AnnotationUtils.getAnnotation(SimpleAlias.class, ComponentScan.class);
System.out.println(ann.value()[0]);
System.out.println(ann.basePackages()[0]);
}
}
结果都是com.binecy
有了AliasFor的好处是,如果我们只需要指定basePackages,可以使用value属性,并且省略value属性
@ComponentScan("com.binecy")
value可以省略,basePackages等价于value,因此,也可以省略,完整的用法是
@ComponentScan(basePackages="com.binecy")
如果除了basePackages,还有其他属性,可以使用
@ComponentScan(basePackages = "com.binecy", lazyInit = true)
将value属性换成basePackages,更明确清晰。
2.2 跨注解的属性别名
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Service#value为@Component#value的别名,@Service#value的值可以映射到@Component#value。
(这里我们将@Service,@Component看做一种特殊的继承关系,@Component是父注解,@Service是子注解,@Service#value覆盖@Component#value)
demo:
package com.my.test;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@Service("serviceAlias")
public class ServiceAlias {
public static void main(String[] args) {
//打印父Component.class,结果不为null,只是属性为null
Component component = AnnotationUtils.getAnnotation(ServiceAlias.class, Component.class);
System.out.println(component);
//通过merge打印父Component.class,结果不为null,属性有值
Component component2 = AnnotatedElementUtils.getMergedAnnotation(ServiceAlias.class, Component.class);
System.out.println(component2);
//打印Service.class自身,结果不为null,属性有值
Service component3 = AnnotationUtils.getAnnotation(ServiceAlias.class, Service.class);
System.out.println(component3);
//打印不相关的Controller.class,结果为null
Controller component4 = AnnotationUtils.getAnnotation(ServiceAlias.class, Controller.class);
System.out.println(component4);
}
}
输出:
@org.springframework.stereotype.Component(value=)
@org.springframework.stereotype.Component(value=serviceAlias)
@org.springframework.stereotype.Service(value=serviceAlias)
null
注意版本,本文是spring-boot 2.2.1.RELEASE,对应spring 5.2.1.RELEASE,其他版本可能稍有不同
可以看到,虽然ServiceAlias上只有@Service,但通过AnnotationUtils.getAnnotation方法会解析得到@Component,而通过AnnotatedElementUtils.getMergedAnnotation方法还可以将@Service#value的值赋给@Component#value。
3. @SpringBootApplication
我们都知道@SpringBootApplication注解,等于@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration三个注解的组合。
Spring是怎样将三个注解的整合到一个注解的呢?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
通过@AliasFor,即使用户使用的是@SpringBootApplication,
Spring还是可以通过AnnotationUtils#getAnnotation,AnnotatedElementUtils#getMergedAnnotation等方法,解析到@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan等注解,并取得对应属性。
理解这点对后面看SpringBoot源码帮助很大。