Spring注解编程模式


概况

多年来,Spring Framework不断发展对注解、元注解和组合注解的支持。 本文档旨在帮助开发人员(Spring的最终用户以及Spring Framework和Spring组合项目的开发人员)开发和使用Spring注解。


本文档的目标

本文档的主要目标包括以下内容的解释:

  • 如何使用Spring注解。
  • 如何开发用于Spring的注解。
  • Spring如何找到注解(即Spring的注解搜索算法如何工作)。

不属于本文档目标内容

本文档的目的不是解释Spring Framework中特定注解的语义或配置选项。有关特定注解的详细信息,建议开发人员查阅对应的Javadoc或参考手册的相应部分。


术语


元注解

元注解是在另一个注解上声明的注解。如果一个注解使用其他注解进行注解,因此,这个注解被元注解。例如,声明要记录的任何注解都是使用java.lang.annotation包中的@Documented进行元注解的。


构造型注解

构造型注解是一种注解,用于声明组件在应用程序中扮演的角色。 例如,Spring Framework中的@Repository注解是任何满足存储库角色或构造型(也称为数据访问对象或DAO)的类的标记。

@Component是任何Spring管理组件的通用构造型。使用@Component注解的任何组件都是作为等待被扫描的组件。类似地,任何使用@Component进行元注解的注解,那么其注解的组件也是作为将要被扫描的组件。 例如,@Service使用@Component进行元注解。

Core Spring提供了几种开箱即用的构造型注解,包括但不限于:@Component@Service@Repository@ Controller@RestController@Configuration@Repository@Service等是@Component的特化。


组合注解

组合注解是使用一个或多个注解进行元注解的注解,其目的是将与这些元注解相关联的行为组合到单个自定义注解中。 例如,使用Spring的@Transactional@Service注解进行元注解的名为@TransactionalService的注解是一个组合注解,它结合了@Transactional@Service的语义。@TransactionalService在技术上也是一个自定义构造型注解。


注解存在

直接存在,间接存在和存在的术语与Java 8中java.lang.reflect.AnnotatedElement的类级别Javadoc中定义的含义相同。

在Spring中,如果注解被声明为元素上存在的其他注解上的元注解,则注解被认为是元素上的元存在。 例如,给定前面提到的@TransactionalService,我们会说@Transactional在任何直接用@TransactionalService注解的类上都是元存在的。


属性别名与覆盖

一个属性别名是从一个注解属性到另一个注解属性的别名。 一组别名中的属性可以互换使用,并视为等效。 属性别名可以按如下分类:

  • 显式别名:如果一个注解中的两个属性通过@AliasFor声明为彼此的别名,则它们是显式别名。
  • 隐式别名:如果一个注解中的两个或多个属性通过@AliasFor声明为元注解中同一属性的显式覆盖,则它们是隐式别名。
  • 传递隐式别名:在一个注解中给出两个或多个属性,通过@AliasFor声明为元注解中属性的显式覆盖,如果属性有效地覆盖符合传递规律的元注解中的相同属性,则它们是传递性的 隐含别名。

一个属性覆盖是一种注解属性,它覆盖(或隐藏)元注解中的注记属性。 属性覆盖可以按如下分类:

  • 隐式覆盖:给定注解@One中的属性A和注解@Two中的属性A,如果@One使用@Two进行元注解,则注解@One中的属性A是注解@Two中属性A的隐式覆盖,仅基于命名约定(即两个属性都命名为A)。
  • 显式覆盖:如果属性A在@AliasFor的元注解中被声明为属性B的别名,则A是B的显式覆盖。
  • 传递显式覆盖:如果注解@One中的属性A是注解@Two中属性B的显式覆盖,而属性B是注解@Three 中的属性C的显式样覆盖,则A是遵循传递定律的C的传递显式覆盖。

例子

Spring Framework和Spring组合项目中的许多注解都使用@AliasFor注解来声明属性别名和属性覆盖。 常见的例子包括来自Spring MVC的@ RequestMapping@GetMapping和@PostMapping,以及来自Spring Boot的@SpringBootApplication@SpringBootTest等注解。


使用@AliasFor声明属性别名

Spring Framework 4.2引入了最好的支持,用于声明和查找注解属性的别名。 @AliasFor注解可用于在单个注解中声明一对别名属性,或者将自定义组合注解中的一个属性的别名声明为元注解中的属性。

例如,spring-test模块的@ContextConfiguration声明如下。

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 {}; // ...
}

Spring Composed和Spring Polyglot

Spring Composed项目是一个用于Spring Framework 4.2.1及更高版本的组合注解的集合。 在那里你会发现@Get@Post@Put@Delete等注解,它们是@GetMapping@PostMapping@PutMapping@DeleteMapping注解的灵感来源,这些注解现在是Spring MVC和Spring WebFlux的一部分。

请随意参考spring-composed项目以获得更多示例和灵感,了解如何实现自己的自定义组合注解,以及进一步展示@AliasFor功能带来的极客幽默和娱乐,请查看Spring Polyglot


常见问题解答

1)@AliasFor可以与@Component@Qualifier的值属性一起使用吗?

最简洁的答案是不。

@AliasFor不会影响@Qualifier和构造型注解中的值属性(例如@Component@Repository@Controller和任何自定义构造型注解)。 原因是这些价值属性的特殊处理在@AliasFor发明之前的几年就已经存在。 因此,由于向后兼容性问题,根本不可能将@AliasFor与这些值属性一起使用。


尚未涵盖的主题

  • 记录关于类、接口、方法、字段、参数和注解的注解和元注解的一般搜索算法。

    • 如果注解在本地和元注解中都存在于元素中会发生什么?
    • @Inherited在注解(包括自定义组合注解)上的存在如何影响搜索算法?
  • 对通过@AliasFor配置的注解属性别名的文档支持。

    • 如果在一个注解实例(具有相同值或具有不同值)中声明属性及其别名,会发生什么?

      • 通常会抛出AnnotationConfigurationException。
  • 对组合注解的文档支持

  • 对在组合注解中元注解属性覆盖的文档支持

    • 记录查找属性时使用的算法,具体说明:

      • 基于命名约定的隐式映射(即,组合注释声明具有与重写的元注解中声明的完全相同的名称和类型的属性)
      • 使用@AliasFor显式映射
      • 如果属性及其中一个别名在注解层次结构中的某处声明,会发生什么? 哪一个优先?
      • 一般,如何解决涉及注解属性的冲突?

英文原版

翻译:Evan Leung

[Spring Annotation Programming Model]https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model

跟Evan学Sprign编程思想 | Spring注解编程模式【译】的更多相关文章

  1. 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework

    小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...

  2. Spring 注解(一)Spring 注解编程模型

    Spring 注解(一)Spring 注解编程模型 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 注解系列 ...

  3. Spring 注解编程之注解属性别名与覆盖

    前两篇文章咱聊了深入了解了 Spring 注解编程一些原理,这篇文章我们关注注解属性方法,聊聊 Spring 为注解的带来的功能,属性别名与覆盖. 注解属性方法 在进入了解 Spring 注解属性功能 ...

  4. 理解 Spring 注解编程模型

    理解 Spring 注解编程模型 Spring 中有一个概念叫「元注解」(Meta-Annotation),通过元注解,实现注解的「派生性」,官方的说法是「Annotation Hierarchy」. ...

  5. Spring 注解编程之模式注解

    Spring 框架中有很多可用的注解,其中有一类注解称模式注解(Stereotype Annotations),包括 @Component, @Service,@Controller,@Reposit ...

  6. 编程思想—面向切面编程(AOP)

    谈到面向切面的编程,我们很容易关联到面向对象编程(OOP).个人对这两种编程方式的解释为:两种编程思想只是站在编程的角度问题. OOP注重的是对象,怎么对对象行为和方法的抽象.如何封装一个具有完整属性 ...

  7. java编程思想-java注解

    注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据. 一.定义注解 注解的定义看起来很像接口的定义.事实上,与其他任何Java接口一样, ...

  8. Java编程思想-基于注解的单元测试

    Junit的测试方法命名不一定以test开头 上面介绍的atunit已经很老了,现在junit测试框架已经基本注解了

  9. Spring 注解编程之 AnnotationMetadata

    在上篇文章 Spring 注解编程之模式注解 中我们讲到 Spring 模式注解底层原理,依靠 AnnotationMetadata 接口判断是否存在指定元注解. 这篇文章我们主要深入 Annotat ...

随机推荐

  1. 使用 Postman 测试你的 API

    使用 Postman 测试你的 API Intro 最近想对 API 做一些自动化测试,看了几个工具,最后选择了 postman,感觉 postman 的设计更好一些,我们可以在请求发送之前和请求获取 ...

  2. 微信支付-小程序H5 公众号 Payment SDK

    前言 今天是2020年一天,去年最后一个月开发了订单和支付系统,尤其在支付系统和微信对接的时候遇到了很多坑,这里给大家总结下,以免大家遇到相同的问题还浪费大量时间 微信支付前期准备 微信商户号,需要商 ...

  3. [02]java数据类型和运算符等知识

    00 Java中的注释 为了方便程序的阅读,Java语言允许程序员在程序中写上一些说明性的文字,用来提高程序的可读性,这些文字性的说明就称为注释.注释不会出现在字节码文件中,即Java编译器编译时会跳 ...

  4. 一文MyBatis-Plus快速入门

    目录 一.依赖及配置 1.在idea中创建一个SpringBoot项目,在pom.xml中添需要的依赖 2.配置数据库连接 3.在启动类中添加注解 @MapperScan 扫描Mapper接口包 4. ...

  5. Cent OS防火墙配置端口开放

    CentOS 6内置的防火墙为iptables,Cent OS7,内置的防火墙则是firewalld iptables 防火墙设置 1.打开/关闭/重启防火墙 #开启防火墙(重启后永久生效): chk ...

  6. SpringCloudAlibaba通过jib插件打包发布到docker仓库

    序言 在SpringBoot项目部署的时候,我了解到了Jib插件的强大,这个插件可以快速构建镜像发布到我们的镜像仓库当中去.于是我打算在毕设当中加上这个功能,并且整合到github actions中去 ...

  7. Spring Data JPA使用findAllOrderBy时踩的坑

    Spring Data JPA使用findAllOrderBy时踩的坑 按照以往的编程经验,我的写法是这样的: List<ActivityEntity> findAllOrderByWis ...

  8. 解决Maven项目中的无故报错的方法

    解决Eclipse+maven中的无故报错 错误: One or more constraints have not been satisfied. Deployment Assembly跟java版 ...

  9. Spring AOP 基于AspectJ

    简介 AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持.因为Spring1.0的时候Aspectj还未出现; AspectJ1.5中新增了对 ...

  10. 图解kubernetes调度器预选设计实现学习

    Scheduler中在进行node选举的时候会首先进行一轮预选流程,即从当前集群中选择一批node节点,本文主要分析k8s在预选流程上一些优秀的筛选设计思想,欢迎大佬们指正 1. 基础设计 1.1 预 ...