Spring 注解编程之模式注解

楼下小黑哥 小黑十一点半

阅读本文大概需要 5.5 分钟。

Spring 框架中有很多可用的注解,其中有一类注解称模式注解(Stereotype Annotations),包括 @Component, @Service, @Controller, @Repository 等。只要在相应的类上标注这些注解,就能成为 Spring 中组件(Bean)。

需要配置开启自动扫描。如在 XML 中配置` 或使用注解 @ComponentScan。

从最终的效果上来看, @Component, @Service, @Controller, @Repository 起到的作用完全一样,那为何还需要多个不同的注解?

从官方 wiki 我们可以看到原因。

A stereotype annotation is an annotation that is used to declare the role that a component plays within the application. For example, the @Repository annotation in the Spring Framework is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO).

不同的模式注解虽然功能相同,但是代表含义却不同。

标注 @Controller 注解,这类组件就可以表示为 WEB 控制层 ,处理各种 HTTP 交互。标注 @Service 可以表示为内部服务层 ,处理内部服务各种逻辑。而 @Repository 可以代表示为数据控制层,代表数据库增删改查动作。

这样一来不同模式注解带来了不同的含义,清晰将服务进行分层。

除了上面的作用,特定的模式注解,Spring 可能会在未来增加额外的功能语义。如现在 @Repository 注解,可以增加异常的自动转换功能。

所以,对于分层服务最好使用各自特定语义的模式注解,如 WEB 层就使用 @Controller注解。

模式注解原理

在 Spring 中任何标注 @Component 的组件都可以成为扫描的候选对象。另外任何使用 @Component 标注的注解,如 @Service,当其标注组件时,也能被当做扫描的候选对象。。

@Component is a generic stereotype for any Spring-managed component. Any component annotated with @Component is a candidate for component scanning. Similarly, any component annotated with an annotation that is itself meta-annotated with @Component is also a candidate for component scanning. For example, @Service is meta-annotated with @Component.


如果想使自定义的注解也能如 @Service 注解功能一样,只要在自定义注解上标注 @Component 就可以。

AnnotationMetadata

从上面文档看出只要在类上存在 @Component注解,即使存在于注解的注解上,Spring 都将能其成为候选组件。

注解上的注解 Spring 将其定义为元注解(meta-annotation),如 @Component标注在 @Service上, @Component 就被称作为元注解。后面我们就将注解的注解称为元注解。

A meta-annotation is an annotation that is declared on another annotation. An annotation is therefore meta-annotated if it is annotated with another annotation. For example, any annotation that is declared to be documented is meta-annotated with @Documented from the java.lang.annotation package.

那么对于一个类是否可以成为 Spring 组件,需要判断这个类是否包含 @Component 注解,或者类上元注解中是否包含 @Component。

在 Spring 中可以通过 MetadataReader 获取 ClassMetadata 以及 AnnotationMetadata,然后获取相应元数据。

ClassMetadata 可以获取类的各种元数据,比如类名,接口等。

而 AnnotationMetadata 可以获取当前类上注解的元数据,如注解名字,以及元注解信息等。

所以只要获取到 AnnotationMetadata,就可以判断是否存在 @Component。判断方式如下

获取 AnnotationMetadata

这里我们从 XML 配置开启扫描开始讲起。

<context:component-scanbase-package="xxx.xxx.xx"/>

首先在 META-INF 下查找 spring.handles 文件。

不明白小伙伴们可以查看上一篇文章 缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制


context 标签在 ContextNamespaceHandler 注册 XML 解析器。在ContextNamespaceHandler中其使用了 ComponentScanBeanDefinitionParser真正解析 XML。

在 ComponentScanBeanDefinitionParser#parse 方法中,首先获取 XML 中配置 base-package属性,获取扫描的范围,然后调用 ClassPathBeanDefinitionScanner#doScan 获取 base-package 所有 BeanDefinition。

在 doScan 方法中最终会调用 ClassPathScanningCandidateComponentProvider#scanCandidateComponents 获取扫描范围内所有 BeanDefinition

<img src="https://img.hacpai.com/file/2019/06/carbon44-3fe12346.png" alt=" doScan" />

在 scanCandidateComponents中首先获取扫描包范围内资源对象,然后迭代从可读取资源对象中MetadataReaderFactory#getMetadataReader(resource) 获取MetadataReader` 对象。

<img src="https://img.hacpai.com/file/2019/06/carbon45-ad7207f0.png" alt=" scanCandidateComponents" />

上文已经讲到 MetadataReader 对象作用,这里查看如何使用 MetadataReader 进行判断。

筛选组件

在 isCandidateComponent方法中将会传入 MetadataReader 到 TypeFilter#match进行判断。

<img src="https://img.hacpai.com/file/2019/06/carbon46-3c58666c.png" alt=" isCandidateComponent" />

条件的判断主要使用 excludeFilters与 includeFilters 两个字段决定。那两个字段从何处生成?

原来在 ComponentScanBeanDefinitionParser中调用 ClassPathBeanDefinitionScanner构造方法时,默认传入 useDefaultFilters=true。

在 registerDefaultFilters 注册默认的过滤器,生成 excludeFilters与 includeFilters初始值。

<img src="https://img.hacpai.com/file/2019/06/carbon47-e68d111c.png" alt=" registerDefaultFilters" />

默认情况下, excludeFilters 将会是个空集,而 includeFilters 集合中增加一个包含 @Component 类型信息的 AnnotationTypeFilter 实例,以及另外两个包含 Java EE 注解 AnnotationTypeFilter 实例。

跳到 AnnotationTypeFilter#match 方法中。AnnotationTypeFilter 类图如下。

AnnotationTypeFilter#match 方法在抽象类 AbstractTypeHierarchyTraversingFilter中实现。

match 方法首先调用了 matchSelf,而该方法最终由 AnnotationTypeFilter 重写。

<img src="https://img.hacpai.com/file/2019/06/carbon49-d6c9313a.png" alt=" matchSelf" />

可以看到这里最终使用 AnnotationMetadata 方法判断是否存在指定注解。

源码分析就到此为止,下篇文章将会深入 AnnotationMetadata,查看其实如何获取元数据的。

References

[1] 异常的自动转换功能: https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#orm-exception-translation
[2] 缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制: https://juejin.im/post/5d06018b518825276a286a3d
[3] Spring Annotation Programming Model: https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model
[4] beans-stereotype-annotations: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-stereotype-annotations

©著作权归作者所有:来自51CTO博客作者mb5ff59251db416的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. IDEA Debug 无法进入断点的解决方法
  2. 使用类型注解让 Python 代码更易读
  3. libp2p-rs kad 使用及调试方法
  4. 只会爬虫不会反爬虫?动图详解利用 User-Agent 进行反爬虫的原理和
  5. 自然语言处理中句子相似度计算的几种方法
  6. 如何不再当分母?我告诉你一个方法
  7. WIN10从休眠中唤醒,总是要点“登录”才能进桌面的解决方法
  8. 高效方法 | Jupyter Notebook 比你想象中的还要强大
  9. 还在抱怨pandas运行速度慢?这几个方法会颠覆你的看法

随机推荐

  1. eclipse android安装
  2. android framework 层服务 分析
  3. Android开发之GridView属性介绍
  4. mac 安装Android studio遇到的问题及解决
  5. Android设备管理器漏洞
  6. Android(安卓)水波效果原理与实现
  7. 后台动态添加布局文件、控件与动态设置属
  8. Android调用系统摄像头拍照并剪裁压缩
  9. android 中文 api (43) —— Chronometer
  10. Android studio 无法启动安卓模拟器