阅读本文大概须要 5.5 分钟。html
Spring 框架中有不少可用的注解,其中有一类注解称模式注解(Stereotype Annotations),包括 @Component
, @Service
, @Controller
, @Repository
等。只要在相应的类上标注这些注解,就能成为 Spring 中组件(Bean)。java
须要配置开启自动扫描。如在 XML 中配置` 或使用注解 @ComponentScan。git
从最终的效果上来看, @Component
, @Service
, @Controller
, @Repository
起到的做用彻底同样,那为什么还须要多个不一样的注解?github
从官方 wiki 咱们能够看到缘由。spring
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
能够表明示为数据控制层,表明数据库增删改查动做。app
这样一来不一样模式注解带来了不一样的含义,清晰将服务进行分层。框架
除了上面的做用,特定的模式注解,Spring 可能会在将来增长额外的功能语义。如如今 @Repository
注解,能够增长异常的自动转换功能。ide
因此,对于分层服务最好使用各自特定语义的模式注解,如 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
就能够。
从上面文档看出只要在类上存在 @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
。判断方式以下
这里咱们从 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
,查看其实如何获取元数据的。
[1]
异常的自动转换功能: https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#orm-exception-translation[2]
缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制: http://www.javashuo.com/article/p-fpshdbbl-hx.html[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