一、创建SqlSessionTemplate与SqlSessionFactory
我们知道spring-boot-starter是通过加载spring.factories文件里的配置类来自动注入的。mybatis-spring-boot-starter下的mybatis-spring-boot-autoconfigure下的META-INF/spring.factories里配置了MybatisAutoConfiguration。
- MybatisAutoConfiguration的sqlSessionFactory方法注入了SqlSessionFactoryBean,通过SqlSessionFactoryBean的getObject方法完成了Configuration的解析与SqlSessionFactory(DefaultSqlSessionFactory)的创建。
- MybatisAutoConfiguration的sqlSessionTemplate方法通过上面创建的sqlSessionFactory来完成SqlSessionTemplate的创建。
SqlSessionTemplate包含DefaultSqlSessionFactory包含Configuration。
SqlSessionTemplate是在MyBatis自带的DefaultSqlSession的基础上增加了对sqlSession的管理,帮助创建和缓存sqlSession,具体看SqlSessionInterceptor。
二、创建MapperProxy
我们用的Mapper其实是经过jdk动态代理后的MapperProxy,创建流程如下:
- 我们通常在主类里用@MapperScan注解来加载mapper。SpringBoot容器在invokeBeanFactoryPostProcessors时,其中一个BeanDefinitionRegistryPostProcessor(简称BDRPP)——ConfigurationClassPostProcessor会解析配置类,因为@MapperScan里面有@Import,所以它会解析注解@MapperScan里的@Import(MapperScannerRegistrar.class)。然后执行MapperScannerRegistrar的registerBeanDefinitions方法,注册MapperScannerConfigurer的BD,并传入mapper的路径basePackage。
- 还是在invokeBeanFactoryPostProcessors方法里。执行MapperScannerConfigurer的postProcessBeanDefinitionRegistry(因为它也是BDRPP)。创建扫描器ClassPathMapperScanner(继承自ClassPathBeanDefinitionScanner),执行scan方法,它的doScan先调用父类的doScan方法,加载basePackage下的mapper.class文件,创建BD,然后执行processBeanDefinitions方法:设置BD的构造方法参数为mapper的类名(这里是字符串,但spring在设置参数时会根据类型自动转为对应的Class);设置BD的beanClass为MapperFactoryBean;如果@MapperScan没有指定sqlSessionTemplateRef和sqlSessionFactoryRef的话,设置BD的AutowireMode为AUTOWIRE_BY_TYPE。以上就完成了MapperFactoryBean的BD的注册。
- 到了创建bean的时候,上面说MapperFactoryBean的BD设置了AUTOWIRE_BY_TYPE,根据spring的自动注入,MapperFactoryBean的setSqlSessionFactory与setSqlSessionTemplate就会被调用,进行注入。MapperFactoryBean类图:
sqlSessionTemplate就在SqlSessionDaoSupport里。他实现了InitializingBean,所以完成创建之后执行DaoSupport的afterPropertiesSet方法:
// DaoSupport
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
// 看MapperFactoryBean的checkDaoConfig实现
checkDaoConfig();
// Let concrete implementations initialize themselves.
try {
// 空方法
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}
// MapperFactoryBean的checkDaoConfig实现
protected void checkDaoConfig() {
// SqlSessionDaoSupport的实现里里检查sqlSessionTemplate是否存在
super.checkDaoConfig();
// 检查mapperInterface
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
// 取出Configuration
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 添加mapper
// 里面是调用mapperRegistry.addMapper(type);
// 创建MapperProxyFactory,放到一个HashMap里,并解析Mapper
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
初始化完了,接着是getObject():
// MapperFactoryBean
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
// SqlSessionTemplate
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
// Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 取出前面创建的MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 实例化
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// MapperProxyFactory,用jdk动态代理创建MapperProxy
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
三、MapperProxy原理
MapperProxy实现了InvocationHandler,我们看invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 一般不成立
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 一般是创建PlainMethodInvoker
// 然后invoke里面mapperMethod.execute(sqlSession, args);
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 根据method找到MapperMethodInvoker并缓存
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
// 如果是default方法,那就直接用该方法,一般不走这个逻辑
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 否则创建一个MapperMethod,用PlainMethodInvoker包装。
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
可以看到最终是调用的MapperMethod的execute方法。再点进去看,它里面的会根据方法的类型,返回结果类型,调用对应的SqlSession的增删改查方法,这里也就是SqlSessionTemplate的增删改查方法。而SqlSessionTemplate里的增删改查方法又是用它的sqlSessionProxy干的,那这个sqlSessionProxy是个啥玩意呢?我们先看一下SqlSessionTemplate的构造方法:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// 调下面的构造方法
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
// 再调下面的构造方法
this(sqlSessionFactory, executorType,
new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
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;
// jdk动态代理
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
就是jdk动态代理出一个SqlSession,看SqlSessionInterceptor:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取DefaultSqlSession,里面有ThreadLocal逻辑,具体看下面第三节
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// DefaultSqlSession的方法
Object result = method.invoke(sqlSession, args);
// 没开启spring事务,那就提交事务
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
}
// 省略其他代码...
}
}
三、事务
如果开启了spring事务,那么Service会被AOP代理增强,增强的逻辑在实现了MethodInterceptor的TransactionInterceptor里。
执行它的invoke方法,里面调了父类的invokeWithinTransaction方法。
如果是声名式事务:
- 创建事务上下文
- AbstractPlatformTransactionManager.doBegin()开始事务:通过数据源获取连接,封装成ConnectionHolder;用连接开启事务;然后以数据源为key,ConnectionHolder为value存到TransactionSynchronizationManager(简称tsm)的ThreadLocal<Map<Object, Object>>(简称tl)里。
- AbstractPlatformTransactionManager.prepareSynchronization()设置tsm的一些属性,表示开始了事务
- invocation.proceedWithInvocation()执行service方法,里面执行MapperProxy的方法。
- 出现异常的话,completeTransactionAfterThrowing()如果配了对应的事务异常,那就回滚,否则提交
- cleanupTransactionInfo()清理事务的相关ThreadLocal里的信息
- commitTransactionAfterReturning() 提交事务
问题来了,连接被保存到tsm的tl里了,那mybatis执行的时候是如何取到这个连接的呢?
回顾一下,mybatis里执行的时候,获取连接是在BaseExecutor的getConnection方法里,里面调用transaction.getConnection(),
这个transaction其实是SpringManegedTransaction,是在创建SqlSessionFactory时设置的,即SqlSessionFactoryBean.buildSqlSessionFactory()里面的:
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
SpringManegedTransaction.getConnection方法调用openConnection()方法,里面调用DataSourceUtils.getConnection方法,
再调doGetConnection方法,可以看到是用数据源从tsm的tl里拿出连接,也就是设置事务的那个连接。
关于SqlSession
还记得在SqlSessionInterceptor的invoke方法里,如果没开启事务,那么就执行sqlSession的提交:
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (holder != null) && (holder.getSqlSession() == session);
}
isSqlSessionTransactional从tsm里用sessionFactory获取sqlSessionHolder,这个是在啥时候塞进去的?
看getSqlSession方法:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 用sessionFactory从tsm的tl里取出SqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
// 如果有就返回
if (session != null) {
return session;
}
// 否则创建sqlSession,然后registerSessionHolder
LOGGER.debug(() -> "Creating a new SqlSession");
session = sessionFactory.openSession(executorType);
// 判断是否开启spring事务,是的话创建SqlSessionHolder塞到tsm的tl里。
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
registerSessionHolder方法先调用tsm的isSynchronizationActive方法判断是否开启事务,因为AbstractPlatformTransactionManager.prepareSynchronization()时初始化过tsm,所以这里判断出开启事务。接着就创建SqlSessionHolder,跟SqlSessionFactory一并塞到tsm的tl里。