一.MyBatis是怎樣與SpringBoot集成的?
SpringBoot開啟自動(dòng)配置,在ConfigurationClassPostProcessor這個(gè)BeanFactory的后置處理器中,會(huì)讀取spring.factories配置文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration,讀取到所有的自動(dòng)配置類進(jìn)行注冊(cè)。我們引入mybatis-spring-boot-starter之后,在上述BeanFactory的后置處理器中就會(huì)讀取mybatis-spring-boot-autoconfigure-***.jar包中的spring.factories文件:

我們看到有MybatisAutoConfiguration,在這個(gè)類中聲明了SqlSessionFactory和SqlSessionTemplate,這樣我們實(shí)際開發(fā)中就可以直接注入SqlSessionTemplate進(jìn)行開發(fā)了。
二.我們開發(fā)過程中創(chuàng)建的是Mapper接口,那我們注入到Service中的是什么對(duì)象呢?
再使用java配置的時(shí)候,由MapperScan這個(gè)注解來注冊(cè)Mapper。MapperScan注解引入了MapperScannerRegistrar,這是一個(gè)ImportBeanDefinitionRegistrar,我們來看它的registerBeanDefinitions方法,其調(diào)用棧如下:
1.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
2.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.core.annotation.AnnotationAttributes, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.String),該方法中聲明要注冊(cè)的對(duì)象是:MapperScannerConfigurer,繼承關(guān)系如下:

可以看出他是一個(gè)BeanFactory的后置處理器,我們看它的postProcessBeanDefinitionRegistry方法調(diào)用棧:
1.org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry
2.org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
3.org.mybatis.spring.mapper.ClassPathMapperScanner#doScan
4.org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions在這個(gè)方法中,對(duì)于每一個(gè)Mapper,都注冊(cè)了一個(gè)MapperFactoryBean,這是一個(gè)FactoryBean,Bean實(shí)例化的時(shí)候會(huì)調(diào)用它的getObject方法
5.org.mybatis.spring.mapper.MapperFactoryBean#getObject
6.org.mybatis.spring.SqlSessionTemplate#getMapper
7.org.apache.ibatis.session.Configuration#getMapper(從這里開始,回到了MyBatis框架本身)
8.org.apache.ibatis.binding.MapperRegistry#getMapper
9.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
10.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.binding.MapperProxy<T>),在這個(gè)方法中,使用JDK動(dòng)態(tài)代理,創(chuàng)建代理對(duì)象,該代理對(duì)象就是我們Service層的代碼中注入的對(duì)象。
三.MyBatis的SqlSession是非線程安全的,那Spring的SqlSessionTemplate是怎么解決的這個(gè)問題?
SqlSessionTemplate是在自動(dòng)配置的時(shí)候創(chuàng)建的,我們看一下它的創(chuàng)建流程:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
? ? PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
? notNull(executorType, "Property 'executorType' is required");
? this.sqlSessionFactory = sqlSessionFactory;
? this.executorType = executorType;
? this.exceptionTranslator = exceptionTranslator;
? this.sqlSessionProxy = (SqlSession)newProxyInstance(SqlSessionFactory.class.getClassLoader(),
? ? ? new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
看到通過JDK動(dòng)態(tài)代理創(chuàng)建了一個(gè)sqlSessionProxy,后續(xù)數(shù)據(jù)庫的增刪改查都是通過這個(gè)sqlSessionProxy。我們著重看一下SqlSessionInterceptor的invoke方法:
1.org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator):

事務(wù)同步管理器可以認(rèn)為是一個(gè)ThreadLocal的map,這樣同一個(gè)線程在使用SqlSessionTemplate多次對(duì)數(shù)據(jù)庫操作的時(shí)候,取到的是同一個(gè)SqlSession;而不同的線程取到的是不同的SqlSession,所以說SqlSessionTemplate是線程安全的。
四.MyBatis怎么跟Spring聲明式事務(wù)配合完成的數(shù)據(jù)庫事務(wù)內(nèi)的增刪改查操作?
我們知道事務(wù)要在同一個(gè)數(shù)據(jù)庫連接上實(shí)現(xiàn),事務(wù)操作分以下三步:
1.設(shè)置數(shù)據(jù)庫連接的自動(dòng)提交為false
2.通過這個(gè)數(shù)據(jù)庫連接進(jìn)行sql語句執(zhí)行
3.異?;貪L或者提交
以上三步中的1和3是Spring聲明式事務(wù)做的,2是MyBatis做的,它們是怎么配合的呢?其實(shí)只需要保證1、2、3這三步中獲取到的是同一個(gè)數(shù)據(jù)庫連接就好,其實(shí)也是通過事務(wù)同部管理器這個(gè)ThreadLocal的map來實(shí)現(xiàn)的。
下面我們分別跟蹤以下這三步中是如何獲取數(shù)據(jù)庫連接的:
首先看開始事務(wù)也就是上述第1步:
1.org.springframework.transaction.interceptor.TransactionInterceptor#invoke
2.org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
3.org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
4.org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
5.org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction,該方法中有:ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());也就是從事務(wù)同部管理器中獲取連接。
其次我們看第2步執(zhí)行sql的時(shí)候:
我們直接看SqlSessionTemplate對(duì)象在執(zhí)行數(shù)據(jù)庫操作時(shí)所使用的sqlSessionProxy這個(gè)代理對(duì)象的方法執(zhí)行過程:
1.org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator)
3.org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(org.apache.ibatis.session.ExecutorType)
4.org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

5.SqlSesssion通過Executor執(zhí)行sql獲取數(shù)據(jù)庫連接的代碼如下:

6.org.mybatis.spring.transaction.SpringManagedTransaction#getConnection
7.org.mybatis.spring.transaction.SpringManagedTransaction#openConnection
8.org.springframework.jdbc.datasource.DataSourceUtils#getConnection
9.org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

可以看到也算是從事務(wù)同部管理器中獲取連接,也就是同一個(gè)線程中取到的是同一個(gè)連接。
最后看事務(wù)異?;貪L和正常提交:
回滾和提交也都是用第1步創(chuàng)建的數(shù)據(jù)庫連接,從org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction的執(zhí)行流程可以看出:
