再后來,由于注解Annotation的流行,出現(xiàn)了@ComponentScan注解,作用跟component-scan標(biāo)簽一樣,跟@Configuration注解配合使用:
不論是component-scan標(biāo)簽,還是@ComponentScan注解。它們掃描或解析的bean只能是Spring內(nèi)部所定義的,比如@Component、@Service、@Controller或@Repository。如果有一些自定義的注解,比如@Consumer、這個注解修飾的類是不會被掃描到的。這個時候我們就得自定義掃描器完成這個操作。
Spring內(nèi)置的掃描器
component-scan標(biāo)簽底層使用ClassPathBeanDefinitionScanner這個類完成掃描工作的。@ComponentScan注解配合@Configuration注解使用,底層使用ComponentScanAnnotationParser解析器完成解析工作。
ComponentScanAnnotationParser解析器內(nèi)部使用了ClassPathBeanDefinitionScanner掃描器,ClassPathBeanDefinitionScanner掃描器內(nèi)部的處理過程整理如下:
1. 遍歷basePackages,根據(jù)每個basePackage找出這個包下的所有的class。比如basePackage為your/pkg,會找出your.pkg包下所有的class。找出之后封裝成Resource接口集合,這個Resource接口是Spring對資源的封裝,有FileSystemResource、ClassPathResource、UrlResource實現(xiàn)等
2. 遍歷找到的Resource集合,通過includeFilters和excludeFilters判斷是否解析。這里的includeFilters和excludeFilters是TypeFilter接口類型的集合,是ClassPathBeanDefinitionScanner內(nèi)部的屬性。TypeFilter接口是一個用于判斷類型是否滿足要求的類型過濾器。excludeFilters中只要有一個TypeFilter滿足條件,這個Resource就會被過濾。includeFilters中只要有一個TypeFilter滿足條件,這個Resource就不會被過濾
3. 如果沒有被過濾。把Resource封裝成ScannedGenericBeanDefinition添加到BeanDefinition結(jié)果集中
4. 返回最后的BeanDefinition結(jié)果集
TypeFilter接口的定義:
TypeFilter接口目前有AnnotationTypeFilter實現(xiàn)類(類是否有注解修飾)、RegexPatternTypeFilter(類名是否滿足正則表達(dá)式)等。
ClassPathBeanDefinitionScanner繼承ClassPathScanningCandidateComponentProvider類。
ClassPathScanningCandidateComponentProvider內(nèi)部的構(gòu)造函數(shù)提供了一個useDefaultFilters參數(shù):
useDefaultFilters這個參數(shù)表示是否使用默認(rèn)的TypeFilter,如果設(shè)置為true,會添加默認(rèn)的TypeFilter:
我們看到這里includeFilters加上了AnnotationTypeFilter,并且對應(yīng)的注解是@Component。@Service、@Controller或@Repository注解它們內(nèi)部都是被@Component注解所修飾的,所以它們也會被識別。
自定義掃描功能
一般情況下,我們要自定義掃描功能的話,可以直接使用ClassPathScanningCandidateComponentProvider完成,加上一些自定義的TypeFilter即可?;蛘邔憘€自定義掃描器繼承ClassPathScanningCandidateComponentProvider,并在內(nèi)部添加自定義的TypeFilter。后者相當(dāng)于對前者的封裝。
我們就以一個簡單的例子說明一下自定義掃描的實現(xiàn),直接使用ClassPathScanningCandidateComponentProvider。
項目結(jié)構(gòu)如下:
我們直接使用ClassPathScanningCandidateComponentProvider掃描spring.study.componentprovider.bean包下的class:
這里掃描出來的類只有2個,分別是ConsumerWithConsumerAnnotation(被@Consumer注解修飾)和ConsumerWithInterface(實現(xiàn)了IConsumer接口)。ConsumerWithComponentAnnotation使用@Component注解,ConsumerWithNothing沒實現(xiàn)任何借口,沒使用任何注解,ProducerWithInterface實現(xiàn)了IProducer接口;所以這3個類不會被識別。
如果我們要自定義ComponentProvider,繼承ClassPathScanningCandidateComponentProvider類即可。
RepositoryComponentProvider這個類是SpringData模塊提供的,繼承自ClassPathScanningCandidateComponentProvider,主要是為了識別SpringData相關(guān)的類。
它內(nèi)部定義了一些自定義TypeFilter,比如InterfaceTypeFilter(識別接口的TypeFilter,目標(biāo)比較是個接口,而不是實現(xiàn)類)、AllTypeFilter(保存存儲TypeList集合,這個集合內(nèi)部所有的TypeFilter必須全部滿足條件才能被識別)等。





