前言
上一篇介紹了Mapper接口代理的實現(xiàn)原理,今天繼續(xù)剖析SqlSession代理。希望看完大家能搞懂下面問題:
- Spring是如何管理Mapper的Bean,實現(xiàn)線程安全
-
Mybatis自身的sqlSession是否線程安全
spring-mybatis.png
源碼分析
二. SqlSession代理
-
1、創(chuàng)建SqlSessionTemplate
在Spring掃描Mapper創(chuàng)建bean時,可以留意到不僅設(shè)置了className,beanClass,還設(shè)置了sqlSessionFactory屬性。
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//將mapper接口的名稱添加到構(gòu)造參數(shù)
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
//設(shè)置BeanDefinition的class
definition.setBeanClass(this.mapperFactoryBean.getClass());
//添加屬性addToConfig
definition.getPropertyValues().add("addToConfig", this.addToConfig);
//添加屬性sqlSessionFactory
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
......
}
}
MapperFactoryBean繼承于SqlSessionDaoSupport,當(dāng)設(shè)置sqlSessionFactory屬性時會觸發(fā)setSqlSessionFactory()方法。
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
/**
* Set MyBatis SqlSessionFactory to be used by this DAO. Will automatically create SqlSessionTemplate for the given
* SqlSessionFactory.
*
* @param sqlSessionFactory
* a factory of SqlSession
*/
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
最終會為每個Mapper Bean創(chuàng)建一個SqlSessionTemplate對象,因此SqlSessionTemplate在Mapper Bean之間是不共享的。
- 2、創(chuàng)建動態(tài)代理
再看看SqlSessionTemplate的構(gòu)造函數(shù)
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
...
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
//創(chuàng)建sqlSession代理
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
SqlSessionTemplate是實現(xiàn)了SqlSession接口的類?;仡櫳弦黄?,Mapper Bean實際調(diào)用的代理類MapperProxy,invoke()方法里用到的sqlSession就是SqlSessionTemplate。然而SqlSessionTemplate自己也不親自操作,而是使用動態(tài)代理替自己完成sqlSession操作
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSession sqlSessionProxy;
...
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
}
@Override
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
...
}
順而言之,關(guān)注點來到處理類SqlSessionInterceptor。它是SqlSessionTemplate私有內(nèi)部類。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//獲取當(dāng)前線程事務(wù)的SqlSession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
...
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
- 3、sqlSession線程管理
SqlSessionInterceptor的invoke方法獲取Sqlsession的方式并不簡單。奧妙在于getSqlSession()方法。它是Spring專門管理sqlSession的工具類SqlSessionUtils里的靜態(tài)方法。
public final class SqlSessionUtils {
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
...
//從當(dāng)前線程獲取SqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//從SqlSessionHolder獲取SqlSession,引用次數(shù)加1
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
//如果當(dāng)前線程沒有sqlSession,創(chuàng)建一個
session = sessionFactory.openSession(executorType);
//將新的sqlSession注冊當(dāng)前線程
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
可以看出sqlSession是由SqlSessionHolder管理的。只要搞懂sqlSession如何創(chuàng)建與注冊,獲取的事就再簡單不過了。重心放在最后兩行代碼sessionFactory.openSession(executorType)和registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);。
-
4、openSession()
實現(xiàn)了SqlSessionFactory的接口有兩個:DefaultSqlSessionFactory和SqlSessionManager。
SqlSessionManager還實現(xiàn)了SqlSession類,是供用戶單獨用Mybatis時使用的。MyBatis為了將SqlSession管理權(quán)交給Spring。將SqlSession由SqlSessionTemplate來實現(xiàn),SqlSessionFactory使用DefaultSqlSessionFactory。
SqlSession.png
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 創(chuàng)建事務(wù)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
//新建一個DefaultSqlSession對象作為SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
DefaultSqlSessionFactory不僅創(chuàng)建sqlSession還創(chuàng)建了事務(wù)。
- 5、registerSessionHolder
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
...
Environment environment = sessionFactory.getConfiguration().getEnvironment();
...
//將新的sqlSession與sqlSessionHolder綁定
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
//將新的sqlSessionHolder與線程綁定
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager
.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
//引用次數(shù)加一
holder.requested();
...
}
registerSessionHolder是SqlSessionUtils的私有靜態(tài)方法,會創(chuàng)建新的SqlSessionHolder與當(dāng)前線程綁定。
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
}
}
bindResource()實現(xiàn)原理也很簡單。將SessionFactory作為key,SqlSessionHolder為value存入一個map里,然后再將這個map存入ThreadLocal。因此,Spring調(diào)用Mybatis每個線程調(diào)用的是不同的SqlSessionHolder即不同的SqlSession,Spring就是這樣管理sqlSession,實現(xiàn)線程安全的。
總結(jié)
經(jīng)過兩篇文章對mybatis源碼的層層剖析,可知。Mapper Bean是通過MapperFactoryBean創(chuàng)建MapperProxy代理獲取實現(xiàn)類。MapperProxy用到的SqlSession是SqlSessionTemplate,實際執(zhí)行的是代理處理類SqlSessionInterceptor。執(zhí)行時會從當(dāng)前線程獲取SqlSessionHolder中的SqlSession,實現(xiàn)線程安全。


