本文解析SpringBoot中重要注解@AliasFor注解的作用,对于理解SpringBoot和后面阅读SpringBoot源码都很有帮助。
我们都知道@SpringBootApplication注解,等于@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration三个注解的组合。
Spring是怎样将三个注解的整合到一个注解的呢?
这就要说到@AliasFor了
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")
如果除了basePackages,还有其他属性,可以使用@ComponentScan(basePackages = "com.binecy", lazyInit = true)
将value属性换成basePackages,更明确清晰。
跨注解的属性别名
不仅是一个注解内不同属性可以声明别名,不同注解的属性也可以声明别名(注解可以作用于注解)
@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
@Service("serviceAlias")
public class ServiceAlias {
public static void main(String[] args) {
Component component = AnnotationUtils.getAnnotation(ServiceAlias.class, Component.class);
System.out.println(component);
Component component2 = AnnotatedElementUtils.getMergedAnnotation(ServiceAlias.class, Component.class);
System.out.println(component2);
}
}
输出
@org.springframework.stereotype.Component(value=)
@org.springframework.stereotype.Component(value=serviceAlias)
可以看到,虽然ServiceAlias上只有@Service,但通过AnnotationUtils.getAnnotation方法会解析得到@Component,而通过AnnotatedElementUtils.getMergedAnnotation方法还可以将@Service#value的值赋给@Component#value。
AnnotationUtils#getAnnotation -> synthesizeAnnotation
static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
...
DefaultAnnotationAttributeExtractor attributeExtractor =
new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
// Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a
// synthesizable annotation before (which needs to declare @AliasFor from the same package)
Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class};
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
}
Spring内部实现并不复杂,在java中,注解是使用动态代理类实现,Spring中同理。
回来看@SpringBootApplication,
@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源码帮助很大。
还有@Repeatable注解是jdk8新增的注解,可以将多个注解替换为一个数组注解
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScans {
ComponentScan[] value();
}
ComponentScans中values属性是一个ComponentScan数组,这里@Repeatable表示当配置了多个@ComponentScan时,@ComponentScan可以被@ComponentScans代替(jdk8中支持重复的注解)
@ComponentScan("com.binecy.bean")
@ComponentScan("com.binecy.service")
public class ComponentScansService {
public static void main(String[] args) {
ComponentScans scans = ComponentScansService.class.getAnnotation(ComponentScans.class);
for (ComponentScan componentScan : scans.value()) {
System.out.println(componentScan.value()[0]);
}
}
}
ComponentScansService 上配置了两个ComponentScan,这时两个@ComponentScan可以被解析@ComponentScans。
如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。