MyBatis 源码学习16——MyBatis与Spring整合(下)

四、Mapper动态代理对象注册过程

Spring框架启动时
会扫描指定路径下的Mapper接口,将Mapper接口转换为Spring中的BeanDefinition对象,并且beanClass属性为MapperFactoryBean,
Spring框架在所有的Bean配置转换为BeanDefinition对象后,就会根据BeanDefinition对象的beanClass属性创建Bean的实例。

Spring框架启动后
每个Mapper接口创建一个MapperFactoryBean对象,当我们通过Mapper接口获取Bean时,获取到的是MapperFactoryBean对象的getObject()方法返回的对象。

MapperFactoryBean对象的getObject()方法中会调用SqlSession对象的getMapper()方法创建一个Mapper代理对象。
在这里插入图片描述

MyBatis Spring模块中的MapperScan注解:用于扫描指定包中的Mapper接口并创建Mapper动态代理对象。
该注解的使用方式:

只需要通过basePackages属性指定Mapper接口所在的包路径,然后使用sqlSessionTemplateRef属性指定SqlSessionTemplate对应的Bean名称即可。

MapperScan注解的定义:
在这里插入图片描述

MapperScan注解通过@Import注解导入了一个BeanDefinition注册类MapperScannerRegistrar,MapperScannerRegistrar类的实现:
在这里插入图片描述

MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口(ImportBeanDefinitionRegistrar用于在Spring解析Bean的配置阶段往BeanDefinitionRegistry容器中注册额外的BeanDefinition对象),
重写了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions()方法,重写的方法实现:

1.首先创建了一个ClassPathMapperScanner对象(父类为ClassPathBeanDefinitionScanner),
在这里插入图片描述

2.然后获取MapperScan注解的属性信息,

3.根据MapperScan的annotationClass和markerInterface属性对扫描的Class进行过滤,

4.最后调用ClassPathMapperScanner对象的doScan()方法进行扫描。

ClassPathMapperScanner是ClassPathBeanDefinitionScanner的子类,用于扫描特定包下的Mapper接口,将Mapper接口信息转换为对应的BeanDefinition对象。

ClassPathMapperScanner类doScan()方法的实现:

1.首先调用父类的doScan()方法,将指定包下的Mapper接口信息转换为BeanDefinitionHolder对象,BeanDefinitionHolder中持有一个BeanDefinition对象及Bean的名称和所有别名。

2.所有的Mapper接口转换为BeanDefinitionHolder对象后,接着调用processBeanDefinitions()方法,对所有BeanDefinitionHolder对象进行处理。

3.在processBeanDefinitions()方法中,对所有BeanDefinitionHolder对象进行遍历,获取BeanDefinitionHolder对象中持有的BeanDefinition对象。

4.然后对BeanDefinition对象的信息进行修改,将BeanDefinition对象的beanClass属性设置为MapperFactoryBean,并向BeanDefinition对象中增加几个PropertyValue对象,对应MapperFactoryBean的addToConfig和sqlSessionTemplate等属性。

将BeanDefinition对象的beanClass属性设置为MapperFactoryBean这一步很重要:

当Spring将所有的Bean配置信息转换为BeanDefinition对象后,会根据BeanDefinition对象来实例化Bean;
当BeanDefinition对象的beanClass属性为MapperFactoryBean时,Spring在创建Bean时实例化的是MapperFactoryBean对象;
当我们根据Mapper类型从Spring容器中获取MapperFactoryBean时,获取到的并不是MapperFactoryBean本身,而是MapperFactoryBeann的getObject()方法返回的对象。

同时Spring会根据BeanDefinition对象中的PropertyValues对象对MapperFactoryBean对象进行属性填充,因此可以实现自动注入MapperFactoryBean对象的addToConfig和sqlSessionTemplate属性。

MapperFactoryBean的getObject()方法的实现:调用SqlSession对象的getMapper()方法返回一个Mapper动态代理对象。

五、MyBatis整合Spring事务管理

Spring框架提供了比较完善的事务管理机制,MyBatis与Spring框架整合后,我们依然可以使用Spring的事务管理机制进行事务管理。

Spring框架的事务管理方式有两种,分别为编程式事务管理和声明式事务管理。

编程式事务管理利用Spring中提供的TransactionTemplate来完成,
声明式事务管理是通过AOP声明一个切面实现。

简单介绍一下Spring编程式事务管理,来一个案例:

在这里插入图片描述

MyBatis中也提供了事务管理器的实现,定义了Transaction接口来实现事务管理器的行为:
在这里插入图片描述

MyBatis中的事务管理器有两个作用,获取JDBC中的Connection对象和对事务进行提交或者回滚操作。

Executor组件就是通过Transaction对象获取JDBC的Connection对象,进而完成与数据库的交互。

MyBatis中的Transaction接口有两个实现类,分别为JdbcTransaction和ManagedTransaction。

JdbcTransaction实现类提供了通过JDBC方式进行简单的事务提交和回滚操作,需要我们自己处理程序中的异常。
ManagedTransaction表示MyBatis不进行事务管理,事务由其他框架来管理。

MyBatis整合Spring事务管理器,比较关键的部分就是如何保证执行数据库操作和事务管理器中提交或回滚事务时使用的是同一个Connection对象。

Spring事务管理器中,Connection对象的获取和释放都是通过spring-jdbc模块中的一个工具类DataSourceUtils来完成的。

DataSourceUtils工具类获取Connection对象的过程:
在这里插入图片描述

在doGetConnection()方法中,
在这里插入图片描述
首先调用TransactionSynchronizationManager类的getResource()方法获取一个ConnectionHolder对象,
如果能获取到,则返回ConnectionHolder对象中持有的Connection对象,
如果获取不到,则从数据源对象的连接池中获取一个Connection对象,然后将Connection对象包装成ConnectionHolder对象与TransactionSynchronizationManager进行绑定。

接下来,了解一下TransactionSynchronizationManager绑定ConnectionHolder对象的过程。
首先关注下TransactionSynchronizationManager类的变量resources,是一个ThreadLocal类型的变量,ThreadLocal对象中存放的是一个HashMap对象:
在这里插入图片描述

然后再看下TransactionSynchronizationManager类的bindResource()方法的实现:
在这里插入图片描述

1.首先获取ThreadLocal对象中的Map对象,如果获取不到,则对ThreadLocal中的Map对象进行初始化,

2.然后将bindResource()方法参数中的对象存放到ThreadLocal中。

TransactionSynchronizationManager的getResource()方法

直接根据Key从ThreadLocal对象中获取对应值,这样就保证了在同一个线程中,使用DataSourceUtils工具的getConnection()方法获取到的始终是同一个Connection对象。

MyBatis Spring模块中对MyBatis中的Transaction接口定义了一个新的实现,即SpringManagedTransaction。该类的getConnection()方法,间接使用Spring中的DataSourceUtils工具类获取Connection对象。
在这里插入图片描述

MyBatis Spring模块还提供了SpringManagedTransactionFactory工厂类用于创建SpringManagedTransaction对象。

最近看下SqlSessionFactory对象的构建,调用SqlSessionFactoryBean类的buildSqlSessionFactory()方法,指定Environment对象的transactionFactory属性为SpringManagedTransactionFactory对象:
在这里插入图片描述

这样就保证了MyBatis的Executor组件通过Transaction对象的getConnetion()方法获取到的Connection对象和Spring事务管理器中获取到的Connection对象是同一个对象。

例如,我们使用编程式事务管理时:
在这里插入图片描述

Spring事务管理器中,调用commit()或者rollback()方法提交或者回滚事务使用的Connection对象和MyBatis操作数据库使用的Connection对象是同一个对象。

总结:Spring框架通过Java中的ThreadLocal机制保证同一个线程中获取到的始终是同一个Connection对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值