文章目录
-
- 前言
- 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的整合原理等。相关文章可以参考我开头列举的系列文章,更为系统的进行学习!!!