1. 簡(jiǎn)介
SpringBoot項(xiàng)目中或者 Spring項(xiàng)目中配置<context:component-scan base-package="com.example.demo" />
,那么在IOC 容器初始化階段(調(diào)用beanFactoryPostProcessor階段) 就會(huì)采用ClassPathBeanDefinitionScanner進(jìn)行掃描包下 所有類,并將符合過濾條件的類注冊(cè)到IOC 容器內(nèi)。Mybatis 的Mapper注冊(cè)器(ClassPathMapperScanner) 是同過繼承ClassPathBeanDefinitionScanner,并且自定義了過濾器規(guī)則來實(shí)現(xiàn)的。具體的 調(diào)用過程并不會(huì)在這里說明,只是想在這里描述ClassPathBeanDefinitionScanner是如何 掃描 和 注冊(cè)BeanDefinition的。
2. 作用
ClassPathBeanDefinitionScanner作用就是將指定包下的類通過一定規(guī)則過濾后 將Class 信息包裝成 BeanDefinition 的形式注冊(cè)到IOC容器中。
- 根據(jù)指定掃描報(bào)名 生成匹配規(guī)則。
例如:classpath*:com.example.demo/**/*.class
- resourcePatternResolver(資源加載器)根據(jù)匹配規(guī)則 獲取 Resource[] 。
- Resource數(shù)組中每一個(gè) 對(duì)象 都是對(duì)應(yīng)一個(gè) Class 文件,Spring 用Resource定位資源, 封裝了資源的IO操作。
- 這里的 Resource 實(shí)際類型是 FileSystemResource.
- 資源加載器 其實(shí)就是 容器 本身。
- meteDataFactory根據(jù) Resouce 獲取到 MetadataReader 對(duì)象
- MetadataReader 提供了 獲取 一個(gè)Class 文件的 ClassMetadata 和 AnnotationMetadata 的 操作。
- 根據(jù)過濾器規(guī)則 匹配 MetadataReader中的類 進(jìn)行過濾,比如 是否是Componet 注解標(biāo)注的類。
- 轉(zhuǎn)換 MetadataReader 為 BeanDefinition.
- 將BeanDefinition 注冊(cè)到 BeanFactory.
3. 默認(rèn)的過濾器注冊(cè)
過濾器用來過濾 從指定包下面查找到的 Class ,如果能通過過濾器,那么這個(gè)class 就會(huì)被轉(zhuǎn)換成BeanDefinition 注冊(cè)到容器。
如果在實(shí)例化ClassPathBeanDefinitionScanner時(shí),沒有說明要使用用戶自定義的過濾器的話,那么就會(huì)采用下面的默認(rèn)的過濾器規(guī)則。
注冊(cè)了@Component 過濾器到 includeFiters ,相當(dāng)于 同時(shí)注冊(cè)了所有被@Component注釋的注解,包括@Service ,@Repository,@Controller,同時(shí)也支持java EE6 的javax.annotation.ManagedBean 和 JSR-330 的 @Named 注解。
protected void registerDefaultFilters() {
// 添加Component 注解過濾器
//這就是為什么 @Service @Controller @Repostory @Component 能夠起作用的原因。
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 添加ManagedBean 注解過濾器
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
// 添加Named 注解過濾器
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
4. 執(zhí)行掃描(doScan)
實(shí)際執(zhí)行包掃描,進(jìn)行封裝的函數(shù)是findCandidateComponents,findCandidateComponents定義在父類中。ClassPathBeanDefinitionScanner的主要功能實(shí)現(xiàn)都在這個(gè)函數(shù)中。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
// 1.根據(jù)指定包名 生成包搜索路徑
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//2. 資源加載器 加載搜索路徑下的 所有class 轉(zhuǎn)換為 Resource[]
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
// 3. 循環(huán) 處理每一個(gè) resource
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
// 讀取類的 注解信息 和 類信息 ,信息儲(chǔ)存到 MetadataReader
//
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
// 執(zhí)行判斷是否符合 過濾器規(guī)則,函數(shù)內(nèi)部用過濾器 對(duì)metadataReader 過濾
if (isCandidateComponent(metadataReader)) {
//把符合條件的 類轉(zhuǎn)換成 BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
// 再次判斷 如果是實(shí)體類 返回true,如果是抽象類,但是抽象方法 被 @Lookup 注解注釋返回true
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
//省略了 部分代碼
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
5. 自定義掃描器
通過自定義的掃描器,掃描指定包下所有被@MyBean 注釋的類。
5.1 定義一個(gè)注解,并注釋一個(gè)類
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyBean {
}
@MyBean
public class TestScannerBean {
}
5.2 編寫掃描器
class MyClassPathDefinitonScanner extends ClassPathBeanDefinitionScanner{
private Class type;
public MyClassPathDefinitonScanner(BeanDefinitionRegistry registry,Class<? extends Annotation> type){
super(registry,false);
this.type = type;
}
/**
* 注冊(cè) 過濾器
*/
public void registerTypeFilter(){
addIncludeFilter(new AnnotationTypeFilter(type));
}
}
5.3 測(cè)試自定義掃描器
- 測(cè)試代碼
@Test
public void testSimpleScan() {
String BASE_PACKAGE = "com.example.demo";
GenericApplicationContext context = new GenericApplicationContext();
MyClassPathDefinitonScanner myClassPathDefinitonScanner = new MyClassPathDefinitonScanner(context, MyBean.class);
// 注冊(cè)過濾器
myClassPathDefinitonScanner.registerTypeFilter();
int beanCount = myClassPathDefinitonScanner.scan(BASE_PACKAGE);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(beanCount);
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
- 測(cè)試結(jié)果
7
//這個(gè)就是我們掃描到的bean
testScannerBean
//下面這些 是 父類掃描器 注冊(cè)的 beanFactory后置處理器
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
6. 總結(jié)
通過對(duì)ClassPathBeanDefinitionScanner的分析,終于揭開了Spring 的類掃描的神秘面紗,其實(shí),就是對(duì)指定路徑下的 所有class 文件進(jìn)行逐一排查,對(duì)符合條件的 class ,封裝成 BeanDefinition注冊(cè)到IOC 容器。
理解ClassPathBeanDefinitionScanner的工作原理,可以幫助理解Spring IOC 容器的初始化過程。
同時(shí)對(duì)理解MyBatis 的 Mapper 掃描 也是有很大的幫助。
因?yàn)?MyBatis 的MapperScannerConfigurer的底層實(shí)現(xiàn)也是一個(gè)ClassPathBeanDefinitionScanner的子類。就像我們自定義掃描器那樣,自定定義了 過濾器的過濾規(guī)則。