Spring 框架中有很多可用的注解, 其中有一类注解称模式注解 (Stereotype Annotations), 包括 @Component, @Service,@Controller,@Repository 等. 只要在相应的类上标注这些注解, 就能成为 Spring 中组件 (Bean).
需要配置开启自动扫描. 如在 xml 中配置 ` 或使用注解 @ComponentScan.
从最终的效果上来看,@Component, @Service,@Controller,@Repository 起到的作用完全一样, 那为何还需要多个不同的注解?
从官方 我们可以看到原因.
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 thejava.lang.annotation package.
那么对于一个类是否可以成为 Spring 组件, 需要判断这个类是否包含 @Component 注解, 或者类上元注解中是否包含 @Component.
在 Spring 中可以通过 MetadataReader 获取 ClassMetadata 以及 AnnotationMetadata, 然后获取相应元数据.
ClassMetadata 可以获取类的各种元数据, 比如类名, 接口等.
而 AnnotationMetadata 可以获取当前类上注解的元数据, 如注解名字, 以及元注解信息等.
所以只要获取到 AnnotationMetadata, 就可以判断是否存在 @Component. 判断方式如下
获取 AnnotationMetadata
这里我们从 xml 配置开启扫描开始讲起.
<context:component-scan base-package="xxx.xxx.xx"/>
首先在 META-INF 下查找 spring.handles 文件.
不明白小伙伴们可以查看上一篇文章 缘起 Dubbo , 讲讲 Spring xml Schema 扩展机制 https://juejin.im/post/5d06018b518825276a286a3d
context 标签在 ContextNamespaceHandler 注册 xml 解析器. 在 ContextNamespaceHandler 中其使用了 ComponentScanBeanDefinitionParser 真正解析 xml.
在 ComponentScanBeanDefinitionParser#parse 方法中, 首先获取 xml 中配置 base-package 属性, 获取扫描的范围, 然后调用 ClassPathBeanDefinitionScanner#doScan 获取 base-package 所有 BeanDefinition.
在 doScan 方法中最终会调用 ClassPathScanningCandidateComponentProvider#scanCandidateComponents 获取扫描范围内所有 BeanDefinition
在 scanCandidateComponents 中首先获取扫描包范围内资源对象, 然后迭代从可读取资源对象中 MetadataReaderFactory#getMetadataReader(resource) 获取 MetadataReader` 对象.
上文已经讲到 MetadataReader 对象作用, 这里查看如何使用 MetadataReader 进行判断.
筛选组件
在 isCandidateComponent 方法中将会传入 MetadataReader 到 TypeFilter#match 进行判断.
条件的判断主要使用 excludeFilters 与 includeFilters 两个字段决定. 那两个字段从何处生成?
原来在 ComponentScanBeanDefinitionParser 中调用 ClassPathBeanDefinitionScanner 构造方法时, 默认传入 useDefaultFilters=true.
在 registerDefaultFilters 注册默认的过滤器, 生成 excludeFilters 与 includeFilters 初始值.
默认情况下, excludeFilters 将会是个空集, 而 includeFilters 集合中增加一个包含 @Component 类型信息的 AnnotationTypeFilter 实例, 以及另外两个包含 Java EE 注解 AnnotationTypeFilter 实例.
跳到 AnnotationTypeFilter#match 方法中. AnnotationTypeFilter 类图如下.
AnnotationTypeFilter#match 方法在抽象类 AbstractTypeHierarchyTraversingFilter 中实现.
match 方法首先调用了 matchSelf, 而该方法最终由 AnnotationTypeFilter 重写.
可以看到这里最终使用 AnnotationMetadata 方法判断是否存在指定注解.
源码分析就到此为止, 下篇文章将会深入 AnnotationMetadata, 查看其实如何获取元数据的.
帮助文档
Spring Annotation Programming Model
『Spring Boot 编程思想』
来源: https://www.cnblogs.com/goodAndyxublog/p/11115345.html