閱讀本文大概須要 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