spring--事務原理、mybatis--MapperScannerConfigurer 和 mybatis--MapperProxy事務,最近想把spring mybatis中的事務和mapper接口的原理分析分析,陸陸續續寫了些,這篇作個總結。html
spring+mybatis mapper接口 聲明式事務配置java
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" lazy-init="false"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:sqlmapper/*Mapper.xml"/> <property name="plugins"> <list> <bean class="***"> <property name="dialect"> <bean class="***"/> </property> </bean> </list> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="*.*.*" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="do*" read-only="false" rollback-for="java.lang.Exception"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pc" expression="execution(* springtry.web.service.*.*(..))"/> <aop:advisor pointcut-ref="pc" advice-ref="txAdvice"/> </aop:config>
ps:以上在service層注入事務,do開頭方法傳播機制是required,其它的是supportsweb
1. org.mybatis.spring.SqlSessionFactoryBean解析mapper xml配置,根據namespace添加class mapper組裝Configuration對象,設置它的environment(包含springManagedTransactionFactory),最後getObject()包裝出DefaultSqlSessionFactory;spring
2. org.mybatis.spring.mapper.MapperScannerConfigurer掃描basePackage下的mapper接口,每一個接口封裝爲MapperFactoryBean(包含封裝了DefaultSqlSessionFactory的SqlSessionTemplate),註冊給spring容器。sql
ps:當調用"mapper接口"時去DefaultSqlSessionFactory-》Configuration得到接口類的代理類express
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
3. 前面兩步是系統的前提工做。mybatis
從service層方法開始,首先進入TransactionInerceptor事務攔截器,由 DataSourceTransactionManager 生成一個DataSourceTransactionObject事務對象,封裝在DefaultTransactionStatus中,調用doBegin開啓這個事務對象,設置這個事務對象 setConnectionHolder(new ConnectionHolder(Connection ex)),把這個ConnectionHolder 以鍵值對綁定在當前線程上 <this.getDataSource(),txObject.getConnectionHolder()>:app
TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());post
返回這個 DefaultTransactionStatus對象,封裝在TransactionAspectSupport.TransactionInfo中,綁定在當前線程上,以後調用實際service方法ui
4. service方法中會注入mybatis mapper接口,調用時實際調用
MapperFactoryBean-》SqlSessionTemplate-》DefaultSqlSessionFactory-》Configuration.getMapper(mapper接口, SqlSessionTemplate);
返回MapperProxy對mapper接口的代理,接着調用MapperProxy
5. MapperProxy中,根據調用的Method返回一個MapperMethod對象.execute(SqlSessionTemplate this.sqlSession, args),其中調用sqlSession.insert, sqlSession.update,sqlSession.delete,sqlSession.selectList,sqlSession.selectMap,sqlSession.selectOne等,
6. 接下來調用SqlSessionTemplate的方法,實際調用SqlSessionTemplate.sqlSessionProxy(SqlSessionTemplate.SqlSessionInterceptor攔截器攔截的代理),攔截器中使用 SqlSessionUtils 調用
SqlSessionTemplate.DefaultSqlSessionFactory.openSession-》Configuration-》environment-》springManagedTransactionFactory
生成一個SpringManagedTransaction事務對象,封裝在一個Executor(SimpleExecutor/ReuseExecutor/BatchExecutor)中,封裝在DefaultSqlSession中並返回。
封裝在SqlSessionHolder中,並以鍵值對綁定在當前線程<DefaultSqlSessionFactory,SqlSessionHolder>
SqlSessionUtils 返回DefaultSqlSession
7. 調用DefaultSqlSession的(update/select等),其中
MappedStatement e = this.configuration.getMappedStatement(接口名.方法名 statement);
this.executor.query/update
8. 回到第6步生成的Executor,doupdate/doquery時會prepareStatement,其中會getConnection調用SpringManagedTransaction事務對象的getConnection(),
DataSourceUtils.getConnection(this.dataSource);
看 mybatis--MapperProxy事務 最後分析的
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
使用的是第三步當前線程的鍵值對,也就是service層開啓spring事務的connection
其實,神祕的spring事務本質仍是jdbc的connection,跟咱們直接使用jdbc是同樣的,可是作了加強