二、MyBatis Mapper Bean初始化深度解析5-4
四、MapperScannerRegistrar載入mapper bean
在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法中,會從bean註冊器中載入bean:
這裡就包括了上一步被載入進來的MapperScannerRegistrar。loadBeanDefinitionsFromRegistrars的實現如下:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
這裡會去呼叫實現類的registerBeanDefinitions方法去載入bean。前面說過,MapperScannerRegistrar實現了介面ImportBeanDefinitionRegistrar,具有方法:registerBeanDefinitions。因此,載入mapper bean就正式開始了。
MapperScannerRegistrar的registerBeanDefinitions方法實現如下:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(mapperScanAttrs, registry); } }
上述程式碼會去讀取註解@MapperScan的值,雖然@MapperScan有很多屬性可以配置,這裡,我們只配置了value屬性,其值為:
@MapperScan(value = {"com.iwill.mybatis.dao.mapper.ext", "com.iwill.mybatis.dao.mapper.gen"})
方法registerBeanDefinitions裡面會設定掃描器(掃描器為ClassPathMapperScanner)以及配置讀取類的包路徑和註冊掃描規則:
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
.map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
也即是說:註解@MapperScan的value、basePackages、basePackageClasses等屬性配置的包路徑都會被掃描。
配置的掃描過濾器(掃描規則scanner.registerFilters())如下:
我們沒有在@MapperScan裡面指定需要掃描的標記指定註解的類(annotationClass屬性來指定),所以acceptAllInterfaces會為true,即會掃描包路徑下的所有介面,但是package-info結尾的去掉。
doScan方法會掃描出符合指定規則的類,並且設定bean的一些屬性:
findCandidateComponents方法就是找出備選的bean,其內部呼叫scanCandidateComponents,scanCandidateComponents實現如下:
其工作過程:掃描指定包路徑下面的所有.class檔案,然後迴圈判斷是否符合過濾規則。isCandidateComponent的實現如下:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
如果這一步判斷通過了,那麼就會進行第二步判斷:
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
這一步會過濾掉不是介面的類。找到符合條件的介面,就會new一個ScannedGenericBeanDefinition,放入到Set<BeanDefinition>進行返回。返回到doScan方法,裡面會對bean進行一些屬性設定:beanName、scope(預設是singleton)等。
然後會註冊到DefaultListableBeanFactory(就是將beanName與bean放到map中,防止重複載入)。掃描載入了mapper bean以後,processBeanDefinitions方法就會對這些bean進行處理:
這裡設定了bean的beanClass,為後面的設定動態代理打下基礎,beanClass的值為MapperFactoryBean(MapperFactoryBean實現了FactoryBean介面)。
至此,@MapperScan的流程就走完了,mapper介面被初始化為bean(非完整bean,還