Mybatis對(duì)Spring的整合實(shí)現(xiàn)
本文只討論整合Spring,Mybatis是如何整合到Spring生態(tài)中的
接口掃描的MapperScan的實(shí)現(xiàn)和擴(kuò)展
@MapperScan, 元標(biāo)注了@Import注解,導(dǎo)入了一個(gè)MapperScannerRegistrar的Configuration Class , 申明如下
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware
我們都知道ImportBeanDefinitionRegistrar 是Spring注入Configuration Class到容器中的一種常見手段, 常見的還有@Import,ImportSelector.. , 該實(shí)現(xiàn)類的核心邏輯在registerBeanDefinitions()中,如下
//importingClassMetadata 為當(dāng)前標(biāo)注了@Import的Configuration Class的注解元信息
//BeanDefinitionRegistry registry 為當(dāng)前BeanFacatory的引用
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1. 獲取@MapperScan注解的屬性信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 2. 注冊(cè)一個(gè)名為 MapperScannerConfigurer的 Bean到IOC容器中
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
// 具體注冊(cè)Bean的邏輯
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
//1. 構(gòu)造BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//2. 設(shè)置自定義的注解,在后面自定義掃描有大用處
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
//3. 設(shè)置自定義的接口 Class,在后面自定義掃描有大用處
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
//....省略部分PropertyValues的屬性賦值
MapperScannerConfigurer的用處以及實(shí)現(xiàn)原理
是一個(gè)
BeanDefinitionRegistryPostProcessor的實(shí)現(xiàn)類,該類型會(huì)在Spring容器啟動(dòng)刷新時(shí)進(jìn)行回調(diào)
查看源碼發(fā)現(xiàn)其類的聲明如下
//1. 發(fā)現(xiàn)其是一個(gè)BeanDefinitionRegistryPostProcessor , 該類型接口會(huì)在IOC容器刷新的時(shí)候進(jìn)行回調(diào)
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
//2. 能在回調(diào)方法中發(fā)現(xiàn)其核心做了兩件事情
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders(); //解析相關(guān)包名占位符
}
//2.1 創(chuàng)建自定義的ClassPathBeanDefinitionScanner(Spring中@ComponentScan核心處理類)并添加自定義的掃描類型
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
//2.2 進(jìn)行掃描獲取BeanDefinition,并注冊(cè)到容器中
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
接下來(lái)我們來(lái)看ClassPathMapperScanner組件的用處 , 他其實(shí)是擴(kuò)展了spring的@ComponentScan的組件掃描方式,核心看registerFilters()方法,里面添加了要掃描的TypeFilter的方式
public void registerFilters() {
boolean acceptAllInterfaces = true; //1. 這個(gè)標(biāo)志位是否要掃描包下所有的接口
//2. 這里的annotationClass是前面注冊(cè)MapperScannerConfigurer時(shí)傳遞進(jìn)來(lái)的自定義注解屬性
if (this.annotationClass != null) {
// 2.1 這里添加IncludeFilter表示,要添加一個(gè)允許的掃描注解,只要標(biāo)注了該注解就會(huì)被ClassLoader掃描到
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
//3. 這里的annotationClass是前面注冊(cè)MapperScannerConfigurer時(shí)傳遞進(jìn)來(lái)的自定義接口Class
if (this.markerInterface != null) {
//3.1 掃描自定義的接口類型,并且
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
//不能實(shí)現(xiàn)類Class,只能是抽象接口或者抽象類
return false;
}
});
acceptAllInterfaces = false;
}
//4. 如果沒(méi)有自定義注解或者自定義接口掃描,那么添加一個(gè)TypeFilter默認(rèn)全部掃描所有
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
掃描時(shí)如何根據(jù)IncludeFilter/ExcludeFilter進(jìn)行掃描和過(guò)濾?核心方法調(diào)用鏈如下
//調(diào)用鏈
//ClassPathMapperScanner#doScan() -> ClassPathBeanDefinitionScanner#doScan() -> ClassPathScanningCandidateComponentProvider#findCandidateComponents() -> scanCandidateComponents()
//其中scanCandidateComponents()方法具體實(shí)現(xiàn)如下
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//1. 獲取傳遞進(jìn)來(lái)的掃描包路徑
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//2. 使用ResourceLoader加載資源
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//3. 使用ASM進(jìn)行元信息讀取
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//4. 這里很關(guān)鍵里面會(huì)進(jìn)行IncludeFilter和ExcludeFilter的判斷,也是能自定義擴(kuò)展組件掃描的核心方法
if (isCandidateComponent(metadataReader)) {
// 5. 拼裝成BeanDefinition,后面會(huì)給BeanDefinition設(shè)置beanClass為MapperFactoryBean代理對(duì)象
//6. 最后注冊(cè)到IOC容器中,此時(shí)我們已經(jīng)可以使用Mybatis的Mapper來(lái)完成依賴注入和依賴查找了
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
//省略部分無(wú)關(guān)源碼...
其中isCandidateComponent()實(shí)現(xiàn)如下
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) { //遍歷所有的ExcludeFilter,若有匹配的則返回false不進(jìn)行掃描
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//遍歷所有的IncludeFilter,若匹配則進(jìn)行Conditional條件注解判斷,這里includeFilters中就包括了之前
//ClassPathMapperScanner#registerFilters()方法中注冊(cè)的includeFilters。這也是為什么我們配置了
// @MapperScan(basePakages="xxxx")就能掃描到xxx包下的所有類到ioc容器中的所有原理
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}