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

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring 注解系列文章:

  1. Spring 注解(一)Spring 注解编程模型
  2. Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils

一、术语

1.1 元注解

元注解是一种标注在别的注解之上的注解。如果一个注解可以标注在别的注解上,那么这个注解已然是元注解。例如,任何需要被文档化的注解,都应该被 java.lang.annotation 包中的元注解 @Documented 标注。

1.2 Stereotype 注解

译者注:保留 Stereotype 原生词汇;可理解为模式化注解、角色类注解。

Stereotype 注解是一种在应用中,常被用于声明要扮演某种职责或者角色的注解。例如,@Repository 注解用于标注任何履行了 repository 职责角色的类(这种职责角色通常也会被称为 Data Access Object 或者 DAO)。

@Component 是被 Spring 管理的组件的对应注解。任何标注了 @Component 的组件都会在 spring 组件扫描时被扫描到。同样的,任何标注了 被元注解 @Component 标注过的注解 的组件,也会在 Spring 组件扫描时被扫描到。例如,@Service 就是一种被元注解 @Component 标注过的注解。

Spring 核心框架提供了一系列可直接使用的 stereotype 注解,包括但不限于 @Component, @Service, @Repository, @Controller, @RestController, and @Configuration。@Repository, @Service 等等注解都基于 @Component 的更精细化的组件注解。

1.3 组合注解

组合注解是一种被一个或者多个元注解标注过的注解,用以撮合多个元注解的特性到新的注解。例如,叫作 @TransactionalService 的注解就是被 spring 的 @Transactional 和 @Service 共同标注过,而且它把 @Transactional 和 @Service 的特性结合到了一起。另外,从技术上上讲,@TransactionalService 也是一个常见的 Stereotype 注解。

一个注解无论是直接标注还是间接标注一个 bean,这个注解在 java8 的 java.lang.reflect.AnnotatedElement 类注释中所约定的含义和特性都不会有任何改变。

在 Spring 中,如果一个元注解标注了其它注解,其它注解标注了一个 bean,那么我们就说这个元注解 meta-present(间接标注了)这个 bean。以上文提到的 @TransactionalService 为例,我们可以说,@Transactional 间接标注了任何一个标注过 @TransactionalService 的 bean。

二、组合注解实现的基础 @AliasFor

@AliasFor 将注解的一个成员名(Attribute Alias)变为另一个。一个成员有多个别名时,这些别名是平等可交换的。成员别名可以分为以下几种。

  1. Explicit Aliases(显式别名):如果一个注解中的两个成员通过 @AliasFor声明后互为别名,那么它们是明确的别名。
  2. Implicit Aliases(隐式别名):如果一个注解中的两个或者更多成员通过 @AliasFor 声明去覆盖同一个元注解的成员值,它们就是隐含别名。
  3. Transitive Implicit Aliases(传递的隐式别名):如果一个注解中的两个或者更多成员通过 @AliasFor 声明去覆盖元注解中的不同成员,但是实际上因为覆盖的传递性导致最终覆盖的是元注解中的同一个成员,那么它们就是可传递的隐含别名。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor { @AliasFor("attribute")
String value() default ""; @AliasFor("value")
String attribute() default ""; Class<? extends Annotation> annotation() default Annotation.class;
}

可以看到 @AliasFor 自身也使用了别名。注意:显示别名必须成对出现。

注意:使用 @AliasFor 标注的有以下限制:一是属性都必须都有默认值且相等;二是属性的返回值类型也必须相等;三是属性双方都必须指定别名,且不能冲突。

2.1 Explicit Aliases(显式别名)

同一注解内两个属性互相声明为别名,如 @AliasFor 自身的 value 和 attribute 属性。同一注解内部可以省略 annotation 属性。

@Retention(RetentionPolicy.RUNTIME)
@interface ContextConfiguration {
@AliasFor(annotation = ContextConfiguration.class, value = "locations")
// @AliasFor("locations")
String value() default ""; @AliasFor(annotation = ContextConfiguration.class, value = "value")
// @AliasFor("value")
String[] locations() default {};
}

2.2 Implicit Aliases(隐式别名)

使用元注解隐式别名。@XmlTestConfig 的 xmlFiles 属性显示的覆盖了 @ContextConfiguration 的 locations 属性。

// 必须显示的标注元注解
@ContextConfiguration
public @interface XmlTestConfig {
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xmlFiles();
}

可以多个字段同时指定别名,这种情况下 value、groovyScripts、xmlFiles 都互为别名。

@Retention(RetentionPolicy.RUNTIME)
@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 {};
}

隐式别名可以不用成对出现。

2.3 Transitive Implicit Aliases(传递的隐式别名)

别名具有传递性。

@Retention(RetentionPolicy.RUNTIME)
@MyTestConfig
public @interface GroovyOrXmlTestConfig {
@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
String[] groovy() default {}; @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xml() default {};
}

2.4 @AliasFor 和 @Inherited 区别

@Inherited 元注解表示被标注过的 class 的子类所继承。 但注意:一是类并不从它所实现的接口继承;二是方法并不从它所重载的方法继承。 更多详见 java @Inherited注解的作用

注意 @AliasFor 和 @Inherited 区别:

  • @Inherited 强调子类可以继承父类的注解信息(不能获取接口上的注解信息)。
  • @AliasFor 通过别名机制更强调两个注解的关系,类似 java 中类的继承。

如 Spring 中 @Controller、@Repository、@Service 都是基于 @Component 派发出了一系列的注解,但语义更强。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
} // @Controller 是基于 @Component 派发出来的
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}

@Controller 是比 @Component 语义更强,但由此也带来了一系列的问题,现在我需要处理 2 个注解了,以前我只有处理 1 个,怎么解决这个问题呢? @AliasFor 就是用来解决这个问题的,可以只用处理 @Component 注解就将其派发的一系列注解也同时处理了。

三、AnnotationUtils 和 AnnotatedElementUtils

注意直接使用 JDK 的注解是不带有语义的,需要使用 Spring 提供的 AnnotationUtils 或 AnnotatedElementUtils 工具类。

  • AnnotationUtils 解决注解别名,包括显示别名、隐式别名、传递的隐式别名。还可以查的指定注解的属性信息,但不能解决元注解属性覆盖的问题。
  • AnnotatedElementUtils 为 Spring 的元注解编程模型定义了公共 API,并支持注解属性覆盖。如果不需要支持注解属性覆盖,请考虑使用 AnnotationUtils

3.1 AnnotationUtils

用于处理注解,元注解,桥接方法(编译器为通用声明生成)以及超级方法(用于可选注解继承)的常规实用程序方法。请注意,JDK 的内省工具本身并不提供此类的大多数功能。

  • getAnnotations(Method, Class) 在给定类级别查找
  • findAnnotation(Method, Class) 给定方法的整个继承层次结构中的查找查找

(1) 元注解支持

大多数 find() 方法和此类中的一些 get() 方法都支持查找用作元注解的注解。有关详细信息,请参阅此类中每个方法的 javadoc。对于在组合注解中使用属性覆盖的元注解的细粒度支持,请考虑使用 AnnotatedElementUtils 的更具体的方法。

(2) 属性别名

此类中返回注解,注解数组或 AnnotationAttributes 的所有公共方法都透明地支持通过 @AliasFor 配置的属性别名。有关详细信息,请参阅各种 synthesizeAnnotation(..) 方法。

(3) 搜索范围

一旦找到指定类型的第一个注解,此类中的方法使用的搜索算法将停止搜索注解。因此,将默默忽略指定类型的其他注解。

@Controller
public class AliasForTest {
@Test
public void test() {
// 可以获取 @Component
Assert.assertNotNull(AnnotationUtils.getAnnotation(AliasForTest.class, Component.class));
}
}

3.2 AnnotatedElementUtils

用于在 AnnotatedElements 上查找注解,元注解和可重复注解的常规实用程序方法。 AnnotatedElementUtils 为 Spring 的元注解编程模型定义了公共 API,并支持注解属性覆盖。 如果您不需要支持注解属性覆盖,请考虑使用 AnnotationUtils。请注意,JDK 的内省工具本身不提供此类的功能。

(1) 查找与获取语义

  • 获取语​​义(Get semantics)仅限于搜索 AnnotatedElement 上存在的注解(即本地声明或继承)或在 AnnotatedElement 上方的注解层次结构中声明的注解。

  • 查找语义(Find semantics)更加详尽,提供了语义加上对以下内容的支持:

    如果带注解的元素是类,则在接口上搜索

    如果带注解的元素是类,则在超类上搜索

    解析桥接方法,如果带注解的元素是方法

    如果带注解的元素是方法,则在接口中搜索方法

    如果带注解的元素是方法,则在超类中搜索方法

(2) 支持 @Inherited

get 语义之后的方法将遵循 Java 的 @Inherited 批注的约定,另外本地声明的注解(包括自定义组合注解)将优于继承注解。相反,查找语义之后的方法将完全忽略 @Inherited 的存在,因为查找搜索算法手动遍历类型和方法层次结构,从而隐式支持注解继承而不需要 @Inherited。

@Test
@GetMapping(value = "/GetMapping", consumes = MediaType.APPLICATION_JSON_VALUE)
public void test() throws NoSuchMethodException {
Method method = ReflectUtils.findDeclaredMethod(
AliasForTest.class, "test", null); // AnnotationUtils 不支持注解属性覆盖
RequestMapping requestMappingAnn1 = AnnotationUtils.getAnnotation(method, RequestMapping.class);
Assert.assertEquals(new String[]{}, requestMappingAnn1.value());
Assert.assertEquals(new String[]{}, requestMappingAnn1.consumes()); // AnnotatedElementUtils 支持注解属性覆盖
RequestMapping requestMappingAnn2 = AnnotatedElementUtils.getMergedAnnotation(method, RequestMapping.class);
Assert.assertEquals(new String[]{"/GetMapping"}, requestMappingAnn2.value());
Assert.assertEquals(new String[]{MediaType.APPLICATION_JSON_VALUE}, requestMappingAnn2.consumes());
}

参考:

  1. 《Spring注解编程模型》:http://ifeve.com/annotation-programming-model/

每天用心记录一点点。内容也许不重要,但习惯很重要!

Spring 注解(一)Spring 注解编程模型的更多相关文章

  1. Spring MVC项目快速搭建(编程模型)

    1)配置DispatcherServlet前端控制器(web配置) 2)将xml文件路径告诉Spring MVC(DispatcherServlet) 以上两步等价于继承了WebApplication ...

  2. 理解 Spring 注解编程模型

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

  3. Spring AOP:面向切面编程,AspectJ,是基于注解的方法

    面向切面编程的术语: 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象 通知(Advice): 切面必须要完成的工作 目标(Target): 被通知的对象 代理(Pr ...

  4. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  5. Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较

    本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同. 相关引入包等Spring  AOP编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一 ...

  6. Spring -07 -AOP [面向切面编程] - 使用注解@+ AspectJ 方式实现环绕/前/后等通知 -超简洁 --静态代理/动态代理{JDK/cglib}

    1.spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解;使用注解来取代配置文件.1.1 引入xmlns:context ,指定扫描范围 <context:comp ...

  7. 【Spring】16、注解事务 @Transactional

    概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性.Spring Framework对事务管理提供了一致的抽象,其特点如下: 为不同的事务API提供一致的编程模型, ...

  8. Spring MVC 中 @ModelAttribute 注解的妙用

    Spring MVC 中 @ModelAttribute 注解的妙用 Spring MVC 提供的这种基于注释的编程模型,极大的简化了 web 应用的开发.其中 @Controller 和 @Rest ...

  9. spring mvc的@Transactional注解

    转自:https://www.cnblogs.com/yepei/p/4716112.html spring的@Transactional注解详细用法   概述 事务管理对于企业应用来说是至关重要的, ...

随机推荐

  1. eclipse老运行上一个程序之原因总结

    运行eclipse有的时候不运行刚写的类,老是运行别的以前的类,删除了以前的类就啥都不运行.找了好久的原因,最后发现,刚写的类没有main()或者有误.这和java的特点有关,程序的运行总是main( ...

  2. 关于log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).的问题

    解决办法(非长久之计,折中) 将该方法插入到main函数中,可以自行打印日志信息了 BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境.原文链接:htt ...

  3. js 获取 this 的属性 obj[0].getAttribute

    js  获取 this 的属性 obj[0].getAttribute

  4. lvm磁盘分区

    初始分区情况见下: 创建lvm类型磁盘 创建卷pv 添加pv到vg中,vg名vgroup0 创建lv lvcreate -L 2g -n zookeeper vgroup0 在vg vgroup0中创 ...

  5. 【OpenGL】第一个窗口

    包含头文件: #include <GL/glew.h> // GLFW #include <GLFW/glfw3.h> 初始化与配置GLFW: glfwInit(); //初始 ...

  6. Android的框架功能说明

    OkHttp网络框架 Picasso图片缓存框架 ORMLite数据库框架 GreenDao数据库框架

  7. unity项目开发必备插件Asset Hunter 2(资源猎人2)

    unity必备插件 Asset Hunter 2 2.4 , 工程项目过大,垃圾太多之后的清洁利器,能识别 ,移除你用不到的资源 扫码时备注或说明中留下邮箱 付款后如未回复请至https://shop ...

  8. UmBasketella

    UmBasketella http://poj.org/problem?id=3737 Time Limit: 1000MS   Memory Limit: 65536K Total Submissi ...

  9. 食物链(带权&种类并查集)

    食物链 http://poj.org/problem?id=1182 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9326 ...

  10. 并行网络爬虫(C++实现)

    step1 使用socket编程技术,利用http协议,抽取网页中的url,实现简单的爬虫. socket int socket (int domain, int type, int protocol ...