文章目录
-
- 前言
- MapperScannerRegistrar
-
- 使用方式
- 实现原理
- MapperScannerConfigurer
-
- 使用方式
- 实现原理
- 两者区别对比
- 源码解析
-
- MapperScannerRegistrar
- MapperScannerConfigurer
- MapperFactoryBean
- 总结
本文里面涉及到的相关文章
| SpringBoot自动装配系列文章 |
|---|
| @EnableXXX注解+@Import轻松实现SpringBoot的模块装配 |
| 带你拿捏SpringBoot自动装配的核心技术?模块装配(@EnableXXX注解+@Import)+ 条件装配(@ConditionalXXX) |
| 深入探究Spring Boot自动配置原理及SPI机制:实现灵活的插件化开发 |
| MyBatis系列相关相关文章 |
|---|
| 究竟FactoryBean是什么?深入理解Spring的工厂神器 |
| 超硬核解析Mybatis动态代理原理!只有接口没实现也能跑? |
| Mybatis与Spring结合深探——MapperFactoryBean的奥秘 |
| Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析 |
| 源码透析MapperScannerRegistrar和MapperScannerConfigurer的区别及作用 |

前言
在使用Spring Boot和MyBatis整合的时候,我们经常会看到
MapperScannerRegistrar
使用方式
一种典型的使用方式是在 Spring Boot 的启动类或者配置类上添加
@Configuration
@MapperScan(basePackages = "com.apple.mapper")
public class MyAppConfig {
// ...
}
点开
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
关于
@EnableXXX注解+@Import轻松实现SpringBoot的模块装配
@EnableXXX注解+@Import轻松实现SpringBoot的模块装配
@EnableXXX注解+@Import轻松实现SpringBoot的模块装配
实现原理
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
在实际的应用中,
MapperScannerConfigurer
使用方式
在非 Spring Boot 的 Spring 应用或者希望通过 XML 来配置扫描路径的场合更常见。以下是一个 XML 配置示例:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.apple.mapper" />
<!-- 其他可配置项,例如 sqlSessionFactoryBean 名称 -->
</bean>
通过配置
而实际上,当使用 Spring boot,并结合
实现原理
两者区别对比
总体上,
由于
源码解析
MapperScannerRegistrar

注意不同版本的
mybatis-spring 整合依赖包里面对应的MapperScannerRegistrar会有所区别,但是做的事情都是一样的!
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取@MapperScan注解的所有属性值
AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 注册MapperScannerConfigurer的BeanDefinition对象
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
// 创建一个GenericBeanDefinition对象,用来存储MapperScannerConfigurer的相关属性
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MapperScannerConfigurer.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.setSynthetic(true);
// 获取@MapperScan注解的各个属性值,包括basePackages, annotationClass, markerInterface, factoryBean等
AnnotationAttributes attributes = AnnotationAttributes.fromMap(annoMeta.getAnnotationAttributes(MapperScan.class.getName()));
List<String> basePackages = new ArrayList<>();
basePackages.addAll(Arrays.stream(attributes.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(attributes.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(attributes.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
// 将@MapperScan注解的属性值赋给MapperScannerConfigurer对象的属性
beanDefinition.getPropertyValues().add("processPropertyPlaceHolders", true);
beanDefinition.getPropertyValues().add("annotationClass", attributes.getClass("annotationClass"));
beanDefinition.getPropertyValues().add("markerInterface", attributes.getClass("markerInterface"));
beanDefinition.getPropertyValues().add("factoryBean", attributes.getClass("factoryBean"));
beanDefinition.getPropertyValues().add("basePackages", basePackages);
// 将MapperScannerConfigurer的BeanDefinition对象注册到Spring容器中,beanName是根据@MapperScan注解所在的类的名称和序号生成的
registry.registerBeanDefinition(beanName, beanDefinition);
}
从上面的代码可以看出,
MapperScannerConfigurer
更为详细的MapperScannerConfigurer解析可以查看我之前的文章
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 如果已经执行过,就跳过
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// 创建一个ClassPathMapperScanner对象,用来扫描指定包下的Mapper接口
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.setMapperFactoryBeanCustomizer(this.mapperFactoryBeanCustomizer);
// 执行扫描操作,将扫描到的Mapper接口创建成MapperFactoryBean的BeanDefinition对象,并注册到Spring容器中
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackages, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
从上面的代码可以看出,
更为详细的Reconfigurer解析可以查看我之前的文章
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
Mybatis-Spring整合原理:MapperFactoryBean和MapperScannerConfigurer的区别及源码剖析
MapperFactoryBean
更为详细的MapperFactoryBean解析可以查看我之前的文章
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
Mybatis与Spring结合深探——MapperFactoryBean的奥秘

@Override
public T getObject() throws Exception {
// 如果已经创建过代理对象,就直接返回
if (this.mapperProxy != null) {
return this.mapperProxy;
}
// 从SqlSessionTemplate或者SqlSessionFactory中获取SqlSession对象
SqlSession sqlSession = getSqlSession();
// 创建Mapper接口的代理对象,使用了MyBatis的MapperProxyFactory类
this.mapperProxy = new MapperProxyFactory<>(this.mapperInterface).newInstance(sqlSession);
return this.mapperProxy;
}
从上面的代码可以看出,
更为详细的MapperFactoryBean解析可以查看我之前的文章
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
Mybatis与Spring结合深探——MapperFactoryBean的奥秘
总结
- MapperScannerRegistrar是一个实现了ImportBeanDefinitionRegistrar接口的类,它的作用是在Spring容器启动的时候,根据@MapperScan注解的配置,动态地注册MapperScannerConfigurer的BeanDefinition对象到Spring容器中,同时将@MapperScan注解的属性值赋给MapperScannerConfigurer对象的属性。
- MapperScannerConfigurer是一个实现了BeanDefinitionRegistryPostProcessor接口的类,它的作用是在Spring容器初始化完成后,扫描指定包下的Mapper接口,并将它们创建成MapperFactoryBean的BeanDefinition对象,然后注册到Spring容器中。
- MapperFactoryBean是一个实现了FactoryBean接口的类,它的作用是创建Mapper接口的代理对象,并将它返回给Spring容器。
这三个类之间的关系可以用下图表示:
@MapperScan -> MapperScannerRegistrar -> MapperScannerConfigurer -> ClassPathMapperScanner -> MapperFactoryBean -> MapperProxy
通过这个流程,我们就可以实现Mapper接口的自动扫描和注入,简化了持久层的开发,提高了开发效率。当然,这个流程还涉及到了很多其他的细节和原理,比如MyBatis的MapperProxyFactory和MapperProxy类,Spring的ImportBeanDefinitionRegistrar和BeanDefinitionRegistryPostProcessor接口,以及Spring和MyBatis的整合原理等。相关文章可以参考我开头列举的系列文章,更为系统的进行学习!!!