本文内容
概观
术语
例子
常问问题
一, 概观
多年来, Spring Framework 不断发展对注解的支持, 比如元注解和组合注解的支持. 本文档旨在帮助开发人员 (Spring 的最终用户, Spring Framework 和 Spring 组合项目的开发人员) 开发和使用 Spring 注解.
二, 本文档的目标
本文档的主要目标包括以下内容的解释:
如何使用 Spring 注释.
如何开发用于 Spring 的注释.
Spring 如何找到注释(即 Spring 的注释搜索算法如何工作).
三, 本文档的非目标
本文档的目的不是说明 Spring Framework 中特定注释的语义或配置选项. 需要查看相关关特定注释的详细信息, 建议开发人员查阅相应的 Javadoc 或参考官网文档的相应部分.
四, 术语
1. 元注解
元注释是 java 基础注解来声明注解. 因此, 一个注解必然是被元注解而注解的. 例如, 任何注解被声明都是 @Documented 从 java.lang.annotation 包中进行元注释的.
2. 模式注解
模式注解是被用于声明在应用程序中个一个组件的角色. 例如,@Repository 注解在 Spring Framework 中是任何满足存储库角色或构造型 (也称为数据访问对象或 DAO) 的类的标记.
@Component 是任何 Spring 管理组件的通用模式注解. 任何被 @Component 标准的组件均为组件扫描的候选对象. 类 似地, 凡是被 @Component 元标注 (meta-annotated) 的注解, 如 @Service, 当任何组件标注它时, 也被视作组件扫 描的候选对象.
核心 Spring 提供了一些模式注解开箱即用, 包括但不限于:@Component,@Service,@Repository, @Controller,@RestController, 和 @Configuration.@Repository, @Service 等等都是 @Component 的扩展化.
3. 组合注释
组合注解是元注解与相结合的那些元注释相关联成一个单一的自定义注解的行为的意图的一个或多个注解. 例如, 一个名为 @TransactionalService 使用 Spring 里面的 @Transactional 和 @Service 注释进行注解的注解是一个组合注释, 它结合了 @Transactional 和 @Service 的语义. @TransactionalService 是在技术上自定义的一个模式注解.
4. 注释存在
一个注解无论是直接标注还是间接标注一个 bean, 这个注解在 java8 的 java.lang.reflect.AnnotatedElement 类注释中所约定的含义和特性都不会有任何改变.
在 Spring 框架里面, 注释被认为是元存在, 如果注释被声明为一些其他的注释的元注释这是一个元件上存在的元件上. 例如, 鉴于上述 @TransactionalService, 我们可以说,@Transactional 是元存在 于直接与注释的任何类 @TransactionalService.
5. 属性别名和覆盖
一个属性别名是从一个注释属性到另一个注释属性的别名. 一组别名中的属性可以互换使用, 并视为等效. 属性别名可以分类如下.
显式别名: 如果一个注释中的两个属性被声明为彼此的别名 @AliasFor, 则它们是显式别名.
隐式别名: 如果一个注释中的两个或多个属性被声明为元注释中相同属性的显式覆盖 @AliasFor, 则它们是隐式别名.
传递隐式别名: 在一个注释中给出两个或多个属性, 这些属性被声明为元注释中属性的显式覆盖 @AliasFor, 如果属性有效地覆盖了遵循传递定律的元注释中的相同属性 , 则它们是传递隐式别名.
一个属性重写是一个重写 (或阴影) 在元注释的注释属性. 属性覆盖可以分类如下.
隐式覆盖: 给定的属性 A 中的注解 @One 和属性 A 的标注 @Two, 如果 @One 是元注解为 @Two, 然后在属性 A 中的注释 @One 是一个隐含的倍率为属性 A 的标注 @Two 只在命名约定为主(即, 两个属性被命名 A).
显示覆盖: 如果属性 A 被声明为属性的别名 B 在通过元注释 @AliasFor, 则 A 是一个明确的覆盖了 B.
传递明确覆盖: 如果注解 @One 中的成员 A 明确覆盖了注解 @Two 中的成员 B, 而且成员 B 实际覆盖了注解 @Three 中的成员 C, 那么因为覆盖的传递性, 所以成员 A 实际覆盖了成员 C.
五. 例子
Spring Framework 和 Spring portfolio (https://github.com/sbrannen/spring-polyglot) 项目中的许多注释都使用 @AliasFor 注释来声明属性别名和属性覆盖. 常见的例子包括 @RequestMapping, @GetMapping 和 @PostMapping 从 Spring MVC 的以及注释, 比如 @SpringBootApplication 和 @SpringBootTest 从 Springboot 启动.
以下部分提供了演示这些功能的代码段.
1. 使用 @AliasFor 声明属性别名
Spring Framework 4.2 引入了一流的支持, 用于声明和查找注释属性的别名. 该 @AliasFor 注释可被用于声明一对混叠属性内的单个注释或从在自定义由注释一个属性声明的别名在元注释的属性.
例如,@ContextConfiguration 从 spring-test 模块声明如下.
- public @interface ContextConfiguration { @AliasFor("locations")
- String [] value()default {}; @AliasFor("value")
- String [] locations()default {}; // ...
- }
该 locations 属性被声明为属性的别名 value , 反之亦然. 因此, 以下声明 @ContextConfiguration 是等效的.
- @ContextConfiguration("/ test-config.xml")
- public class MyTests { /* ... */ }
- @ContextConfiguration(value = "/ test-config.xml")
- public class MyTests { /* ... */ }
- @ContextConfiguration(locations = "/ test-config.xml")
- public class MyTests { /* ... */ }
类似地, 从元注释中覆盖属性的组合注释可 @AliasFor 用于精确控制在注释层次结构中覆盖哪些属性. 实际上, 甚至可以为 value 元注释的属性声明别名.
例如, 可以使用自定义属性覆盖开发组合注释, 如下所示.
- @ContextConfiguration
- public @interface MyTestConfig {
- @AliasFor(annotation = ContextConfiguration.class, attribute = "value")
- String[] xmlFiles();
- // ...
- }
上面的示例演示了开发人员如何实现自己的自定义组合注释 ; 然而, 以下表明 Spring 本身在许多核心 Spring 注释中使用了这个特性.
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @RequestMapping(method = RequestMethod.GET)
- public @interface GetMapping {
- /**
- * Alias for {@link RequestMapping#name}.
- */
- @AliasFor(annotation = RequestMapping.class)
- String name() default "";
- /**
- * Alias for {@link RequestMapping#value}.
- */
- @AliasFor(annotation = RequestMapping.class)
- String[] value() default {};
- /**
- * Alias for {@link RequestMapping#path}.
- */
- @AliasFor(annotation = RequestMapping.class)
- String[] path() default {};
- // ...
- }
2.Spring Composed 和 Spring Polyglot
Spring Composed 项目是可以在在 spring4.2.1 和更高版本中使用的一系列组合注解. 你可以在 Spring MVC 使用像 @Get,@Post,@Put, 和 @Delet 这样的注解, 也可以在 Spring MVC REST 应用中使用 @GetJson,@PostJson 等等注解.
如果你确信已经学习了足够深入的 spring-composed 例子, 汲取了足够多的灵感, 然后你可以为 spring-Composed 项目贡献出由你自定义的组合注解!
- https://github.com/sbrannen/spring-composed
- https://github.com/sbrannen/spring-polyglot
六, FAQ
1)可以 @AliasFor 与 value 属性一起使用 @Component 和 @Qualifier?
最简洁的答案是: 不可以.
将 value 在属性 @Qualifier 和模式注解 (例如 @Component,@Repository,@Controller, 和任何自定义典型化注解) 不能受其影响 @AliasFor. 原因是这些 value 属性的特殊处理是在 @AliasFor 发明之前的几年. 因此, 由于向后兼容性问题, 根本不可能使用 @AliasFor 这些 value 属性.
七, 待发掘主题
记述 注解和标注了注解和元注解的类, 接口, 成员方法, 成员变量, 参数的通用搜索算法.
如果一个注解既是以注解又是以元注解的方式标注了一个元素会发生什么呢?
一个注解标注了 @Inherited(包括自定义组合注解)后, 是如何对搜索算法产生影响呢?
记述 通过 @AliasFor 配置注解成员别名的技术原理.
如果一个成员和它的别名都声明在一个注解实例 (成员和别名值相同或者不同时) 中在技术上会发生什么?
较有代表性的一种情况是, 一个 AnnotationConfigurationException 将会被抛出.
记述_组合注解_的技术原理.
记述 组合注解成员覆盖元注解成员的原理.
详细记述 查找成员的算法原理:
基于命名约定的间接覆盖(换句话说, 组合注解中有明确名字和类型的成员去覆盖元注解的成员)
使用 @AliasFor 来直接覆盖
如果一个成员和它的众多别名中的一个在注解继承的某一层级中被重新声明了会发生什么? 哪个会生效?
总之, 成员变量声明时的冲突是怎么被解决的?
关注个人公众号: coder 辰砂 ,目前正在慢慢的整理, 前期还是基础技术部分整理
来源: https://www.cnblogs.com/tojian/p/10091054.html