Mybatis对Spring的整合实现
本文只讨论整合Spring,Mybatis是如何整合到Spring生态中的
接口扫描的MapperScan的实现和扩展
@MapperScan
, 元标注了@Import注解,导入了一个MapperScannerRegistrar
的Configuration Class
, 申明如下
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware
我们都知道ImportBeanDefinitionRegistrar
是Spring注入Configuration Class到容器中的一种常见手段, 常见的还有@Import
,ImportSelector
.. , 该实现类的核心逻辑在registerBeanDefinitions()
中,如下
//importingClassMetadata 为当前标注了@Import的Configuration Class的注解元信息
//BeanDefinitionRegistry registry 为当前BeanFacatory的引用
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1. 获取@MapperScan注解的属性信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 2. 注册一个名为 MapperScannerConfigurer的 Bean到IOC容器中
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
// 具体注册Bean的逻辑
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
//1. 构造BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//2. 设置自定义的注解,在后面自定义扫描有大用处
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
//3. 设置自定义的接口 Class,在后面自定义扫描有大用处
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
//....省略部分PropertyValues的属性赋值
MapperScannerConfigurer
的用处以及实现原理
是一个
BeanDefinitionRegistryPostProcessor
的实现类,该类型会在Spring容器启动刷新时进行回调
查看源码发现其类的声明如下
//1. 发现其是一个BeanDefinitionRegistryPostProcessor , 该类型接口会在IOC容器刷新的时候进行回调
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
//2. 能在回调方法中发现其核心做了两件事情
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders(); //解析相关包名占位符
}
//2.1 创建自定义的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 进行扫描获取BeanDefinition,并注册到容器中
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
接下来我们来看ClassPathMapperScanner
组件的用处 , 他其实是扩展了spring的@ComponentScan
的组件扫描方式,核心看registerFilters()
方法,里面添加了要扫描的TypeFilter
的方式
public void registerFilters() {
boolean acceptAllInterfaces = true; //1. 这个标志位是否要扫描包下所有的接口
//2. 这里的annotationClass是前面注册MapperScannerConfigurer时传递进来的自定义注解属性
if (this.annotationClass != null) {
// 2.1 这里添加IncludeFilter表示,要添加一个允许的扫描注解,只要标注了该注解就会被ClassLoader扫描到
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
//3. 这里的annotationClass是前面注册MapperScannerConfigurer时传递进来的自定义接口Class
if (this.markerInterface != null) {
//3.1 扫描自定义的接口类型,并且
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
//不能实现类Class,只能是抽象接口或者抽象类
return false;
}
});
acceptAllInterfaces = false;
}
//4. 如果没有自定义注解或者自定义接口扫描,那么添加一个TypeFilter默认全部扫描所有
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");
});
}
扫描时如何根据IncludeFilter/ExcludeFilter
进行扫描和过滤?核心方法调用链如下
//调用链
//ClassPathMapperScanner#doScan() -> ClassPathBeanDefinitionScanner#doScan() -> ClassPathScanningCandidateComponentProvider#findCandidateComponents() -> scanCandidateComponents()
//其中scanCandidateComponents()方法具体实现如下
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//1. 获取传递进来的扫描包路径
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进行元信息读取
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//4. 这里很关键里面会进行IncludeFilter和ExcludeFilter的判断,也是能自定义扩展组件扫描的核心方法
if (isCandidateComponent(metadataReader)) {
// 5. 拼装成BeanDefinition,后面会给BeanDefinition设置beanClass为MapperFactoryBean代理对象
//6. 最后注册到IOC容器中,此时我们已经可以使用Mybatis的Mapper来完成依赖注入和依赖查找了
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
//省略部分无关源码...
其中isCandidateComponent()
实现如下
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) { //遍历所有的ExcludeFilter,若有匹配的则返回false不进行扫描
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//遍历所有的IncludeFilter,若匹配则进行Conditional条件注解判断,这里includeFilters中就包括了之前
//ClassPathMapperScanner#registerFilters()方法中注册的includeFilters。这也是为什么我们配置了
// @MapperScan(basePakages="xxxx")就能扫描到xxx包下的所有类到ioc容器中的所有原理
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}