本文内容

  1. 概观
  2. 术语
  3. 例子
  4. 常问问题

一、概观

多年来,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();

    // ...
}
@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属性。

七、待发掘主题

  • 记述 注解和标注了注解和元注解的类、接口、成员方法、成员变量、参数的通用搜索算法。
    1. 如果一个注解既是以注解又是以元注解的方式标注了一个元素会发生什么呢?
    2. 一个注解标注了@Inherited(包括自定义组合注解)后,是如何对搜索算法产生影响呢?
  • 记述 通过@AliasFor配置注解成员别名的技术原理。
    1. 如果一个成员和它的别名都声明在一个注解实例(成员和别名值相同或者不同时)中在技术上会发生什么?
    2. 较有代表性的一种情况是,一个AnnotationConfigurationException将会被抛出。
  • 记述_组合注解_的技术原理。
  • 记述 组合注解成员覆盖元注解成员的原理。
    1. 详细记述 查找成员的算法原理:
      • 基于命名约定的间接覆盖(换句话说,组合注解中有明确名字和类型的成员去覆盖元注解的成员)
      • 使用@AliasFor来直接覆盖
    2. 如果一个成员和它的众多别名中的一个在注解继承的某一层级中被重新声明了会发生什么?哪个会生效?
    3. 总之,成员变量声明时的冲突是怎么被解决的?

关注个人公众号:coder辰砂 ,目前正在慢慢的整理,前期还是基础技术部分整理
【译】spring注解编程模型-LMLPHP

12-09 18:04