Mybatis整合Spring实现事务管理的源码分析
一:前言
没有完整看完,但是看到了一些关键的地方,这里做个记录,过程会有点乱,以后逐渐补充最终归档为完整流程;相信看过框架源码的都知道过程中无法完全确定是怎样的流程,毕竟不可能全部都去测试一遍
,但是看的过程中灵感的显现很重要(就是直觉知道接下来的步骤是什么应该是什么之类的,但是这个直觉是猜的而非蒙的,即过程里是有往会递推看到了一些关键点的而不是抛色子来确定是哪个子类)
,因此自己写的时候也无法将看的过程里产生的想法写得很细,过程也有点跳,如果大家有疑问最好自己去验证(方式就是搜索然后看哪里调用了这样一步步看下去就行,有些小技巧就是如果是public那么一般都是由外部类调用[入口],如果是protected却没实现
说明在子类里有实现,如果已经实现说明此方法在父类里会被调用,如果是private那么肯定在当前类会被调用);
二:重要类或方法
1)对于Mybatis
1.org.apache.ibatis.executor.Executor接口,有大概BatchExecutor/ReuseExecutor/SimpleExecutor三个子类有共同的父类BaseExecutor,它是DefaultSqlSession类的组件,sqlSession对象的如selectOne实际上是由executor执行的;
2.org.apache.ibatis.transaction.Transaction接口,它的实现类有多个,但是每个实现类里都有DataSource属性用于产生数据源和数据库服务交互;
3.SpringManagedTransaction是支持Spring管理Mybatis事务的关键;
4.DefaultSqlSessionFactory
5.DefaultSqlSession
它们之间的重要关系:sqlsession(DefaultSqlSession)里有executor(SimpleExecutor);executor里有transaction对象;transaction里有datasource;
流程:sqlsession执行代码委托为executor执行,executor里通过transaction获得connection;而transaction又通过判断threadlocalmap里是否有ConnectionHolder对象;如果没有则通过datasource获得一个,否则返回存在threadlocalmap里的;
三:执行流程
如:sqlSession.selectOne执行流程:(DefaultSqlSession里有Executor接口属性)
3.1selectOne最终转到selectList;
3.4
3.2 selectList最终到BaseExecutor(实现了Executor接口)里的query方法
3.3在BaseExecutor的query方法里执行这一句:list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);,因此这一句代码很关键;
3.4 然后queryFromDatabase里调用BatchExecutor或SimpleExecutor等(BaseExecutor的实现类)的doQuery方法,通过:this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);调用;
3.5接着看SimpleExecutor的doQuery方法(看源码很多时候是靠即时的灵感,比较难完整描述看时的思路和过程);
补充开始:1.接着看stmt = this.prepareStatement(handler, ms.getStatementLog());,很重要,因为statement执行sql语句也是通过它内部的connection对象来执行的;
2.看SimpleExecutor里prepareStatement里面的Connection connection = this.getConnection(statementLog);,
接着看这个getConnection是在BaseExecutor里的:Connection connection = this.transaction.getConnection(); 重要重要,transaction终于在执行sql的过程里出现了;
而Transaction对象是在SimpleExecutor创建的时候就会初始化进去;
3.整合Spring的情况下通过本文的上下文知道此transaction是SpringManagedTransaction对象;
SpringManagedTransaction实现了Transaction接口,然后里面有DataSource接口属性,也有Connection接口属性;
这里通过Transaction获取Connection时会判断这个Connection属性是否有值,有则直接返回,否则通过DataSource属性来获取一个(SpringManagedTransaction#openConnection方法);
【下面开始看Mapper的connection(即Mapper里获取的Connection要能被@Transactional给用到,而如果是先@Transactional创建的Connection也要能被Mapper给识别用到)和@Transactional注解的connection是怎么关联起来的】
4.接着看SpringManagedTransaction的this.openConnection();有代码:
this.connection = DataSourceUtils.getConnection(this.dataSource); this.autoCommit = this.connection.getAutoCommit(); this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
5.我们看DataSourceUtils.getConnection(this.dataSource);,然后转到doGetConnection方法里有代码:
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource); if(conHolder == null ...) {通过dataSource获得一个新的connection} else{ conHolder.getConnection(); //return一个在threadlocalmap里的connection对象; }
注意,上上面的DataSourceUtils和这里当TransactionSynchronizationManager及ConnectionHolder是spring里的类不是mybatis的,里面的threadlocal对象都是静态的,因此mybatis可以获得spring中放在threadlocalmap的connectionholder对象;
【这里再解释一下,这里获取Connection实际上是通过TransactionSynchronizationManager(Spring自带)的来获取,它从ThreadLocal里,通过key是当前的dataSource对象来获取这个dataSource在当前线程下创建的connection对象,通过ResourceHolder包裹(ConnectionHolder);即Mapper里获取Connection最终是通过TransactionSynchronizationManager来实现,
而TransactionSynchronizationManager则是通过dataSource获取到Connection后会保存到TransactionSynchronizationManager的ThreadLocal里;
可以推测@Transactional肯定也是最终通过TransactionSynchronizationManager来获取的Connection,这样两块地方的Connection就关联起来了】
而spring是否产生connectionholder以及是否放到threadlocalmap里是根据txAdvice策略来的;
补充结束;
3.6接着看handler.query(stmt, resultHandler);然后可以挑RoutingStatementHandler的这个方法看;
3.7会委托给其它handler处理,这里可暂定为PreparedStatementHandler类;
3.8看到ps.execute();,后面没什么好看了就是jdk中通过stmt执行sql的方式,这里关键的是stmt里的connection是什么;
@Transactional关联原理:
1.org.springframework.transaction.interceptor.TransactionInterceptor,所有的@Transactional的方法都会被这个来做一个拦截后再执行(拦截原理就是AOP);
2.在执行真正方法之前(@Transactional),会通过transactionManagerCache来获取对应的transactionManager(PlatformTransactionManager接口)【是IOC里的一个对象,所以有名字】;
3.这里获取的PlatformTransactionManager接口具体实现对象可以是DataSourceTransactionManager类型,然后里面是有DataSource属性的,这个是在配置DataSourceTransactionManager就会配置的;
4.然后可以看到流程:TransactionInterceptor#invoke(对@Transactional方法切面后的调用处)里执行其父类:TransactionAspectSupport#invokeWithinTransaction方法;
5.然后TransactionAspectSupport#createTransactionIfNessary(..),然后方法里面有tm.getTransaction(...)(AbstractPlatformTransactionManager类里);
6.然后再跳到:DataSourceTransactionManager#doGetTransaction();然后这里就可以看到用到了TransactionSynchronizationManager来获取ConnectionHolder;
7.DataSourceTransactionManager#doGetTransaction()获得transactionWrapper(DataSourceTransactionObject)后,后续代码还会执行DataSourceTransactionManager#doBegin(..)
8.在doBegin方法里会判断获得的transactionWrapper里包裹的ConnectionHolder是否有值,没有值内部会通过DataSource来获取一个connection,并且也是会set到TransactionSynchronizationManager的ThreadLocal里;
而@Mapper和@Transactional和DataSource的关系则是:
@Mapper会通过DefaultSqlSession,里面有Executor,里面有Transaction,而Transaction里则是有DataSource;
这个是可以在生成@Mapper切面方法的接口实现类的时候就能知道应该用哪个DataSource(默认只有一个数据源自然只有一个DataSource对象,如果多个数据源mapper这里用默认的(注意,多个数据源也是用的一个DataSource,只不过是DynamicDataSource(AbstractRoutingDataSource),然后会有个切面来设置DynamicDataSource里面具体要用哪个DataSource【用ThreadLocal和静态方法来设置】),可以看下:https://blog.csdn.net/jdbcmeng/article/details/126765167),所以这里可以实现当没有ConnectionHolder找到对的DataSource来获取connection,然后绑定到TransactionSynchronizationManager的ThreadLocal里;
而@Transactional则也是一样用默认的数据源DataSource,如果是多数据源,这里也是需要指定用哪个TransactionManager的,而TransactionManager里就有DataSource属性;
四:和Spring整合过程中重要步骤
1.配置了org.mybatis.spring.SqlSessionFactoryBean,默认情况下它内部的transactionFactory属性是SpringManagedTransactionFactory对象(如果不想Spring来管理事务则可以主动指定sqlSessionFactoryBean的transactionFactory为别的类);
2.SqlSessionFactoryBean的sqlSessionFactory的configuration的environment里保存了transactionFactory对象为SpringManagedTransactionFactory对象,这一步很重要;
3.我们来看DefaultSqlSessionFactory的openSessionFromDataSource方法(真正获得sqlSession对象的地方),里面有代码
Environment environment = this.configuration.getEnvironment(); TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); Executor executor = this.configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
注意:execType默认对应的是SimpleExecutor,且这个executor里保存了SpringManagedTransactionFactory产生的SpringManagedTransaction对象,这里先明确一下;
4.上面已经明确了sqlSession执行语句是委托为exector来执行的;后面的步骤和三里的一致了就不赘述了;
五:Spring里事务管理的重要类和方法
1.org.springframework.transaction.interceptor.TransactionInterceptor,极其重要,这个是当执行pointcut方法时将会由这个事务拦截器根据<tx:attributes>配置的propagation做一些额外处理(是一个around),包括是否需要产生connection对象和将这个connection对象的autoCommit设置为什么值(SpringManagedTransaction的this.openConnection();会用到此值),比如REQUIRED就会先判断threadlocalmap里是否有connectionholder,有不做预处理没有则通过datasource获取connection并设置为autoCommit为false,然后通过
connectionholer包装此connection并set到threalocalmap里;
2.org.springframework.jdbc.datasource.DataSourceTransactionManager,极其重要,是spring用来根据propagation策略来执行不同的逻辑的重要组件(在TransactionInterceptor的invoke方法里执行);
3.TxNamespaceHandler用来处理名称空间为tx的元素;
4.TxAdviceBeanDefinitionParser用来解析<tx:advice元素,通过doParse处理;
5.NameMatchTransactionAttributeSource,极其重要,存储了<tx:attributes里配置的策略供TransactionInterceptor使用;
补充:如果要看懂spring的事务管理,可以从TransactionInterceptor的invoke方法实现开始看,这个是入口函数,而内部用到的ConnectionHolder则是和Mybatis有交互的类;
posted on 2018-04-22 17:27 Silentdoer 阅读(423) 评论(0) 收藏 举报