Spring 注解编程之注解属性别名与覆盖
前两篇文章咱聊了深入了解了 Spring 注解编程一些原理,这篇文章我们关注注解属性方法,聊聊 Spring 为注解的带来的功能,属性别名与覆盖。
注解属性方法
在进入了解 Spring 注解属性功能之前,我们先看一个正常 Java 注解。
在注解中,属性方法与其他类/接口方法写法类似,但是存在一些区别。
注解属性方法的返回类型仅限为八种基本类型(包装类不支持),字符串,class,enum,Annotation以及前面类型的数组。
复习一下,java 八种基本类型分别为,byte(字节型)、short(短整型)、int(整型)、long(长整型)、float(单精度浮点型)、double(双精度浮点型)、boolean(布尔型)、char(字符型)。
其次,注解属性方法可以使用
default设置默认值。如果没有设置默认值,声明注解时必须显式设置属性,否则编译将会出错。
另外 Java 注解无法继承类,也无法实现接口。
Spring 属性方法特性
在 Spring 中,有一些注解,使用不同属性方法,却能到达相同结果。典型的如
RequestMapping。
在 WEB 项目中,设置 url 路径,我们可以在方法是这样声明:
@RequestMapping("hello") public String helloAnnotation() { 。。。。 }
上面方法本质使用注解
value属性。当注解声明时只需要设置一个方法时,如果属性方法为 value,不需要使用 key=value 的语法,只需要直接设置属性值即可。
另外也可以使用
path属性方法设置。
@RequestMapping(path = "hello") public String helloAnnotation() { 。。。。 }
两种方式,最后运行效果一致。
查看
RequestMapping注解源码,可以发现在
value与
path属性方法上使用
@AliasFor,并且两个互相指向对方。
Spring 4.2 加入
@AliasFor注解,并使用
@AliasFor重新更新
RequestMapping等注解,为它们内部带来了别名的功能。
@AliasFor
使用方式
在 Spring 中,
@AliasFor可以在同一注解中使用,使用方法如
RequestMapping注解。
这种方式,带来含义明确属性方法。如
RequestMapping,
path属性方法,这个属性方法含义就比较明确,不同的人理解不会有偏差。而
value属性含义就不是很明确,不能一下子就将它真正含义产生联系。
日常开发中,我们也要避免 i,a,b 这些无意义的命名,尽量使用含义明确的命名。这样利用维护代码的人理解。
第二点,同一注解属性方法相互别名,这样就兼容之前版本用法。
RequestMapping注解如果仅新增
path属性,然后根据其解析 url 路径,这样就会导致升级 Spring 版本过程,运行错误的。
一个好软件版本需要时向前兼容,如 JDK 8 兼容 JDK 6一样。
另外
@AliasFor注解还可以作用与不同注解之前,典型的如
SpringBootApplication注解。
SpringBootApplication#scanBasePackages别名与
ComponentScan#basePackages。设置前者间接为后者赋值。
Spring Boot 就是使用
@Aliasfor与组合注解功能,使用
SpringBootApplication一个注解代替
Configuration,
EnableAutoConfiguration,
ComponentScan。
Spring 注解属性覆盖与别名
使用
@AliasFor注解,可以做到别名的功能。
在 Spring 中别名可以分为以下几类:
- 显式别名(xplicit Aliases)
- 隐式别名(Implicit Aliases)
- 传递隐式别名(Transitive Implicit Aliases)
以上三类都需要满足以下条件:
- 属性类型相同
- 属性方法必须存在默认值
- 属性默认值必须相同
否则运行过程中将会出错。
显式别名
如果一个注解中的两个成员通过
@AliasFor声明后互为别名,那么它们是显式别名
。
显示别名的关系如图所示。
隐式别名
如果一个注解中的两个或者更多成员通过
@AliasFor声明去覆盖同一个元注解的成员值,它们就是隐式别名
。
隐式别名如图所示。
上图中,
@One的
name属性与
nameAlias别名与
@Two
nameAlias属性。由于
@One注解中并未直接使用
@AliasFor,所以与
@One注解隐式别名。
隐式别名类似于数学的等式。可以将其看做以下推导过程。
@One.name=@Two.nameAlias @One.nameAlias=@Two.nameAlias 可以推导出 @One.name=@One.nameAlias
传递式隐式别名
如果一个注解中的两个或者更多成员通过
@AliasFor声明去覆盖元注解中的不同成员,但是实际上因为覆盖的传递性导致最终覆盖的是元注解中的同一个成员,那么它们就是传递隐式别名。
传递式隐式别名如图所示。
这种类型涉及了多个注解,
@One#name别名了
@Two#nameAlias属性,然后在
@One#nameAlias属性又别名了
@Three#nameAliasThree属性。然后由于
@Two#nameAlias又别名了
@Three#nameAliasThree属性,这就导致
@One#name与
@One#nameAlias间接才生了关系。这种依靠传递性才生别名关系,称为 传递式隐式别名。
隐式别名类似于数学的等式。大家也可以将其用上面等式推导。
属性覆盖
属性覆盖指的是注解的一个成员覆盖另一个成员,最后两者成员属性值一致。
属性覆盖可以分为三类:
- 隐式覆盖(Implicit Overrides)
- 显示覆盖(Explicit Overrides)
- 传递式显式覆盖(Transitive Explicit Overrides)
隐式覆盖
当一个注解
@One被元注解
@Two标注,两个注解存在同样的属性方法
name。
@Two#name将会被
@One#name属性覆盖。
两个看似不来自不同注解的成员 name 指向了同一个成员 name。
显示覆盖
显示覆盖就比较简单了,使用 @AliasFor 注解之后,就成为显示覆盖。
传递式显式覆盖
如果注解
@One#name显示覆盖了
@Two#nameAlias,而
@Two#nameAlias显示覆盖了
@Three#nameAlias,最后因为传递性,
@One#name实际覆盖了
@Three#nameAlias。
总结
Spring 4.2 新增
@AliasFor注解,带来一些特性。但是要注意的是仅仅存在
@AliasFor不会执行任何语义别名。
底层原理可以参考
AnnotationUtils与
AnnotatedElementUtils。
帮助文档
另外欢迎加入 Java 极客技术知识星球,获取最新 Java 技术。
- Spring组合注解的神奇实用功能详解(功能组合以及别名属性覆盖)
- Web框架梳理:第四章:Spring学习入门、Spring属性注入、AOP编程、注解开发
- 关于spring使用@value注解获取properties文件属性问题
- Spring:Bean的别名(name)和作用域(scope属性)
- spring-04 之IOC 属性注入(注解方式 附代码)
- Spring @Autowired注解私有属性(无set方法,无构造方法可以access此私有属性)
- spring用注解的方式实现属性装配
- Spring @Autowired注解私有属性(无set方法)
- spring中注解属性scope的prototype是什么意思?
- Spring注解开发-属性依赖注入指定名称的bean
- Spring 注解实体类中非数据库字段属性
- Spring-IOC-通过IOC注解对象属性,并在其他层获取该对象。
- Spring注解配置全局自定义@Transactional 属性
- SpringBoot和mongodb开发需要使用到的注解和属性
- Spring使用注解的方式注入属性时的xml文件配置
- spring用注解的方式实现属性装配
- 通过Spring Aop编程思想,利用注解为某些对象添加新的功能
- Spring面向切面编程——Spring实现AOP方式——通过注解实现
- 关于如何 Spring注解的方式 指定parent属性
- Spring配置shiro时自定义Realm中属性无法使用注解注入的解决办法