当前位置: 首页 > news >正文

【mybatis系列】spring是如何整合mybatis将Mapper接口注册为Bean的

【mybatis系列】spring是如何整合mybatis将Mapper接口注册为Bean的

简单来说:

Spring整合MyBatis将Mapper接口注册为Bean的原理,主要是通过MapperScannerConfigurer (实现了BeanDefinitionRegistryPostProcessor接口)(或Spring Boot的自动配置)扫描Mapper接口,创建MapperFactoryBean的BeanDefinition实例,并注册到Spring容器中。MapperFactoryBean通过JDK动态代理为Mapper接口生成代理对象,从而实现了Mapper接口的Bean化。这一过程简化了开发者对数据库操作的管理,提高了开发效率。

参考如下流程:

1. 首先mybatis的mapper接口核心是 JDK动态代理
2. spring扫描类的时候会排除接口和抽象类,这样接口和抽象类是无法注册到IOC容器中
3. mybatis实现了BeanDefinitionRegistryPostProcessor 可以动态注册BeanDefinition
4. 需要自定义扫描器(继承spring内部扫描器 ClassPathBeanDefinitionScanner)重写排除接口的方法(isCandidateComponent)
5. 以上,就完成了接口注册成了BeanDefinition,但是无法实例化Bean,因为接口无法实例化
6 需要将BeanDefinition的BeanClass替换成JDK动态代理的实例对象(偷天换日)
7. mybatis通过FactoryBean的工厂方法设计模式,可以自由控制Bean的实例化过程

spring集成mybatis,核心点:JDK动态代理,BeanDefinitionRegistryPostProcessor, FactoryBean

Spring整合MyBatis将Mapper接口注册为Bean的原理,主要涉及到Spring的Bean注册机制以及MyBatis的Mapper接口处理。这一过程可以分为以下几个关键步骤:

1. 配置数据源和SqlSessionFactory

  • 在Spring的配置文件中(可以是XML配置、Java配置或注解配置),首先需要配置数据源(DataSource),该数据源会被注入到SqlSessionFactory中。
  • 使用SqlSessionFactoryBean来创建SqlSessionFactory实例。SqlSessionFactoryBean实现了FactoryBean接口,能够生成SqlSessionFactory对象。这个过程中,会配置MyBatis的配置文件(如mybatis-config.xml)以及Mapper映射文件等信息。

2. MapperScannerConfigurer扫描Mapper接口

  • MapperScannerConfigurer是Spring和MyBatis整合过程中一个重要的类,它实现了BeanDefinitionRegistryPostProcessor接口。Spring容器启动时会回调其postProcessBeanDefinitionRegistry方法。
  • MapperScannerConfigurer负责扫描指定包路径下的所有Mapper接口。对于每一个扫描到的Mapper接口,MapperScannerConfigurer会创建一个MapperFactoryBean的BeanDefinition实例,并将其注册到Spring容器中。这里,MapperFactoryBean的beanClass属性被设置为Mapper接口对应的类(但实际上是通过JDK动态代理来实现的)。

3. MapperFactoryBean生成代理对象

  • MapperFactoryBean是FactoryBean的一个实现,当Spring容器需要获取Mapper接口的Bean时,会调用MapperFactoryBean的getObject()方法。
  • getObject()方法内部会通过SqlSession的getMapper()方法,基于Mapper接口创建一个JDK动态代理对象。这个代理对象会拦截对Mapper接口方法的调用,并将这些调用转发给MyBatis执行。

4. @MapperScan注解的自动化配置(适用于Spring Boot)

  • 在Spring Boot项目中,通常不需要手动配置MapperScannerConfigurer,因为Spring Boot提供了自动配置。
  • 可以通过在配置类上使用@MapperScan注解来指定Mapper接口所在的包路径,Spring Boot会自动为这些Mapper接口创建Bean,并注册到Spring容器中。
  • 如果未使用@MapperScan注解,并且Mapper接口所在的包不在@ComponentScan的扫描范围内,则需要在Mapper接口上使用@Mapper注解来显式指定该接口是一个Mapper接口,以便Spring Boot能够自动识别并注册。

MapperScannerConfigurer.java
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {...public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {this.processPropertyPlaceHolders();}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.registerFilters();scanner.setMapperHelper(this.mapperHelper);scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));}...}    
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {Iterator var3 = beanDefinitions.iterator();while(var3.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();if (this.logger.isDebugEnabled()) {this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");}definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());definition.setBeanClass(this.mapperFactoryBean.getClass());if (StringUtils.hasText(this.mapperHelperBeanName)) {definition.getPropertyValues().add("mapperHelper", new RuntimeBeanReference(this.mapperHelperBeanName));} else {if (this.mapperHelper == null) {this.mapperHelper = new MapperHelper();}definition.getPropertyValues().add("mapperHelper", this.mapperHelper);}definition.getPropertyValues().add("addToConfig", this.addToConfig);boolean explicitFactoryUsed = false;if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionFactory != null) {definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);explicitFactoryUsed = true;}if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {if (explicitFactoryUsed) {this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionTemplate != null) {if (explicitFactoryUsed) {this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);explicitFactoryUsed = true;}if (!explicitFactoryUsed) {if (this.logger.isDebugEnabled()) {this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");}definition.setAutowireMode(2);}}}protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();}}


http://www.mrgr.cn/news/13416.html

相关文章:

  • 数据结构(6.4_2)——最短路径问题_BFS算法
  • 2024了,Neo4j能显示节点图片吗?
  • 【Linux —— POSIX信号量 - 基于环形队列的生产消费模型】
  • 【云原生kubernetes系列之SkyWalking篇】
  • 每天一个数据分析题(五百零五)- 提升方法
  • k8s POD控制器
  • 网络安全威胁2024年中报告
  • 代码随想录 刷题记录-19 动态规划(3)完全背包理论、习题
  • 《教育学报》
  • 界面交互体验测试的工具
  • 【多线程】volatile关键字探究和System.out.println()隐式地插入内存屏障问题
  • 快速搭建和运行Spring Boot项目的简易指南
  • Kafka线上问题优化
  • qemu 跨架构
  • js导出方式及引入方式
  • 深圳保障房、商品房、小产权房子类型对比
  • 【OpenCV教程】图片隐写方法的种类及其python实现
  • 【Linux】POSIX版本 信号量
  • 【系统架构设计师-2017年】综合知识-答案及详解
  • 用powermock编写单元测试