Spring中@AliasFor注解的作用及原理
本文基于Springboot 2.1.6.RELEASE 版本分析.
关于@AliasFor
注解,曾提过的一个issue Explicit attribute overrides configured via @AliasFor not supported for components picked up via component scanning, 不过从spring 5.2 版本开始已经被修复了
Spring中@AliasFor
注解的作用主要是将一个注解上的属性值传递给另一个注解或者将同一个注解类的属性设置互为别名.
但这并不是java原生支持的,需要通过Spring中提供的工具类
org.springframework.core.annotation.AnnotationUtils
或者org.springframework.core.annotation.AnnotatedElementUtils
来解析。AnnotatedElementUtils
内部还是调用的AnnotationUtils
同一个注解类的属性设置互为别名
示例: 如下是一个自定义注解,两个属性互为别名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableCVS {@AliasFor(value = "address")String value() default "";@AliasFor(value = "value")String address() default "";
}@Configuration
@EnableCVS(address = "hhh")
public class AppConfig {}
因为两个属性互为别名,所以在value没有赋值的情况下,通过AnnotationUtils
仍然可以获取到值,而通过java原生的方式则无法获取。
通过上图也可以发现Spring其实是自己实现了jdk动态的拦截器来实现别名功能.
但是有个小小的细节: 如果同时设置address和value的值通过AnnotationUtils#findAnnotation(Class<?>, annotationType)
获取属性值,则会抛出如下异常
org.springframework.core.annotation.AnnotationConfigurationException:
In annotation [com.example.demo.config.EnableCVS] declared on class com.example.demo.config.AppConfig
and synthesized from [@com.example.demo.config.EnableCVS(value=haha, address=hhh)],
attribute 'address' and its alias 'value' are present with values of [hhh] and [haha],
but only one is permitted.at org.springframework.core.annotation.AbstractAliasAwareAnnotationAttributeExtractor.getAttributeValue(AbstractAliasAwareAnnotationAttributeExtractor.java:103)at org.springframework.core.annotation.SynthesizedAnnotationInvocationHandler.getAttributeValue(SynthesizedAnnotationInvocationHandler.java:91)at org.springframework.core.annotation.SynthesizedAnnotationInvocationHandler.invoke(SynthesizedAnnotationInvocationHandler.java:80)at com.sun.proxy.$Proxy8.address(Unknown Source)
将一个注解上的属性值传递给另一个注解
例如可以自定义一个注解@MyComponentScan
,自定义注解的属性可以传递给Spring中的@ComponentScan
的属性。让自定义注解拥有和@ComponentScan
一样的功能。
除此之外我们还可以通过@Import
注解来实现自己的逻辑,注册自定义bean,让@MyComponentScan
拥有比@ComponentScan
更多的功能。
SpringBoot的启动注解@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
官方文档解释如下:
@AliasFor是一个用于给其它注解属性声明别名的注解。
使用场景
给注解中的属性显式指明别名:在单个注释中,可以在一对属性上声明
@AliasFor
,以表示它们是彼此可以互换的别名。 (个人注释:等价的关系,给a赋值,则b也有相同的值,反之亦然,但在使用时不能同时赋值)。给元注释中属性的显式别名:如果
@AliasFor
的注释属性设置为与声明它的注释不同的注释,则该属性将被解释为元注释中属性的别名(即显式元注释属性重写)。这样就可以精确地控制在注解层次结构中覆盖哪些属性。实际上,使用@AliasFor
甚至可以为元注释的value
属性声明别名。注释中的隐式别名:如果注解中的一个或多个属性被声明和同另一注解中的属性相同(直接或传递),则这些属性将被视为彼此的一组隐式别名,作用类似于注释中显式别名的行为。
使用要求
与Java中的任何注释一样,仅仅存在@AliasFor
本身并不能强制实现别名语义。要强制执行别名语义,必须通过AnnotationUtils
中的实用工具方法加载注解。在幕后,Spring会将注解封装在动态代理中来合成注解,动态代理透明地强制使用标记@AliasFor
的注解属性别名语义。类似地,在注释层次结构中使用@AliasFor
时,AnnotatedElementUtils
支持显式元注释属性重写。通常您不需要自己手动合成注释,因为在Spring管理的组件上查找注释时,Spring将透明地为您这样做。
实施要求
注释中的显式别名:
- 构成别名对的每个属性都必须用@AliasFor注释,并且属性或值必须引用别名对中的另一个属性。
- 别名属性必须声明相同的返回类型。
- 别名属性必须声明默认值。
- 别名属性必须声明相同的默认值。
- 不应声明批注。
元批注中属性的显式别名:
- 作为元注释中属性的别名的属性必须使用
@AliasFor
进行注释,并且属性必须引用元注释中的属性。 - 别名属性必须声明相同的返回类型。
- 注释必须引用元注释。
- 引用的元注释必须在声明
@AliasFor
的注释类上存在。
批注中的隐式别名:
- 属于一组隐式别名的每个属性都必须使用
@AliasFor
进行注释,并且属性必须引用同一元注释中的同一属性(直接或通过注释层次结构中的其他显式元注释属性重写进行传递)。 - 别名属性必须声明相同的返回类型。
- 别名属性必须声明默认值。
- 别名属性必须声明相同的默认值。
- 注释必须引用适当的元注释。
- 引用的元注释必须在声明
@AliasFor
的注释类上存在。
示例:注释中的显式别名
在@ContextConfiguration
中,value
和locations
是彼此的显式别名。
public @interface ContextConfiguration {@AliasFor("locations")String[] value() default {};@AliasFor("value")String[] locations() default {};// ...
}
示例:元注释中属性的显式别名
在@XmlTestConfig
中,xmlFiles
是@ContextConfiguration
中locations
的显式别名。换句话说,xmlFiles
覆盖@ContextConfiguration
中的locations
属性。
@ContextConfiguration
public @interface XmlTestConfig {@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] xmlFiles();
}
示例:注释中的隐式别名
在@MyTestConfig
中,value
、groovyscript
和xmlFiles
都是@ContextConfiguration
中locations
属性的显式元注解属性重写。因此,这三个属性也是彼此的隐式别名。
@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 {};
}
示例:注释中的传递隐式别名
在@GroovyOrXmlTestConfig
中,groovy
是对@MyTestConfig
中groovyscript
属性的显式重写;然而,xml是@ContextConfiguration中locations属性的显式重写。此外,groovy
和xml
是相互传递的隐式别名,因为它们都有效地重写了@ContextConfiguration
中的locations
属性
@MyTestConfigpublic @interface GroovyOrXmlTestConfig {@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")String[] groovy() default {};@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")String[] xml() default {};}
Spring中@AliasFor注解的作用及原理相关推荐
- Spring中常用注解及其作用
1.@Component 它是这些注解里面最普通的一个注解,一般用于把普通pojo实例化到spring容器中. @Controller和@Service和@Repository是它的特殊情况,当一个类 ...
- Spring中常用注解及作用
1.@Component 类注解,设置该类为spring管理的bean,属性可以定义bean的id,添加在类定义的上方,@Component注解不可以添加在接口上,因为接口无法创建对象. eg: 步骤 ...
- Spring中@Import注解的作用和使用
查看Import注解源码 作用: 1.声明一个bean 2. 导入@Configuration注解的配置类 3.导入ImportSelector的实现类 4.导入ImportBeanDefinitio ...
- Spring中异步注解@Async的使用、原理及使用时可能导致的问题
前言 最近,很多同学碰到了下面这个问题,添加了Spring提供的一个异步注解@Async循环依赖无法被解决了,下面是一些读者的留言跟群里同学碰到的问题: 本着讲一个知识点就要讲明白.讲透彻的原则,我决 ...
- Spring中常用注解的介绍
spring中使用注解时配置文件的写法: <?xml version="1.0" encoding="UTF-8"?> <span style ...
- 声明式事务、Spring 中常用注解、Ajax
五. 声明式事务 编程式事务: 1.1 由程序员编程事务控制代码. 1.2 OpenSessionInView 编程式事务 声明式事务: 先引入依赖 <dependency><gro ...
- spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志
spring中自定义注解(annotation)与AOP中获取注解 一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. packag ...
- Spring boot热部署的作用和原理
Spring boot热部署的作用和原理 前言 一.Spring boot热部署的作用 1.什么是SpringBoot热部署 2.什么项目重启 3.什么静态文件 4.如何使用Spring boot热部 ...
- Spring 中所有注解
Spring中的注解主要分为两类: 类级别的注解: 如@Component.@Repository.@Controller.@Service以及JavaEE6的@ManagedBean和@Named注 ...
最新文章
- java跨函数跳转_C语言中将绝对地址转换为函数指针以及跳转到内存指定位置处执行的技巧...
- 一些零碎知识(域名、DNS、浏览器、动态静态页面、web应用系统工作原理)
- 时间序列举例--------协方差+相关系数+随机游走+平稳性
- golang操作redis
- Mysql的innodb缓冲池管理(转)
- mysql链表_MySql链表语句--博客园老牛大讲堂
- Altium Designer(AD18)常用操作和快捷方式
- linux httppost 请求接口参数被截断_记一次小程序图片安全接口和CountDownLatch的使用...
- SuperMap iDesktop之导入数据
- Happy 牛 Year!牛年dotnet云原生技术趋势
- h5点击后字体加粗出现下边框_人力资源管理论文格式(字体+版式+打印)
- 如何复制图文消息封面图片?正文没显示
- XJTUSE专业课与实验指南(已经开源)
- java——》Supplie
- Java 根据当前日期,获取到月初,和月底时间
- Linux 安装qq农场小游戏
- 互联网晚报 | 8月14日 | ​爱马仕回应16.5万自行车已抢光;​曝苹果新一代不再支持3.5mm耳机插孔;​元宇宙招聘潮开启...
- 财务学python还是vba_各位大佬好,财务分析,要使用BI,VBA,Python,Wind学习那个可以对财务工作更加有益?...
- android 加速软件,手机技巧:千万不要用安卓手机加速App
- java学习笔记——springmvc 之 数据自定义转换器 数据格式化 JSR303数据校验返回与接收JSON(@RequestBody 和 @ResponseBody)