以下文章为本人阅读源码时的随笔记录,比较混乱且不一定正确,仅供参考。
源码版本
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
开始
首先找入口处,也就是基于Spring自己实现的SPI机制。这个是springboot的基础,我就不细说了,直接定位到下图位置:
第一个明显是啥语言方向的,果断进第二个org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration。我们知道mybatis跟数据库交互都是通过sqlsession这个类去交互的。随便写个DEMO DEBUG可知,sqlsession接口的实现类是SqlSessionTemplate,如下图。
好,那么我们就看看啥时候把这个sqlsession配置进去的即可,回到配置类,找到如下代码:
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
继续跟进去看一下,但是现在有一个executorType 的判断,鬼知道他会进哪一个构造方法呢,直接上断点;好,是第二个
最后的构造函数代码如下:
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());
}
看得出来SqlSessionTemplate这玩意主要就是持有sqlSessionFactory 、sqlSession的代理对象了。
其实在这里,最重要的就是SqlSessionInterceptor。sqlSession代理对象的作用,也全在这个拦截器身上了。
重新DEBUG跟一下一个简单的查询过程,首先是MapperProxy类的invoke方法,
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
继续跟MapperMethod的execute()方法,我是selectList所以进到这里,这里不是重点,继续下去
executeForMany的具体实现,注意看走的是我注解写的地方,这里开始调用sqlsession进行查询了
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
//断点走到了这里
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
继续走,进入了sqlsession的实现类,sqlsessionTemplete
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
在往里走,终于到了代理类了,继续走,
最后他还是交给的DefaultSqlSession去执行的,
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
然后DefaultSqlSession又交给executor,看到这,其实已经有点偏题了,但是没关系,顺便看看mybatis的一级缓存跟二级缓存把,CachingExecutor的query方法,先通过sql,入参啥的获取到一个cacheKey,后面应该就是拿这个CacheKey去缓存里面匹配。
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续走,发现了一个缓存的地方,这个就是二级缓存,这个是需要手动开启的,也就是这行代码ms.isUseCache()。
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续走到了BaseExeutor的query方法,缓存代码在这,localCache.getObject(key),很明显,用这个缓存的话没有任务先前逻辑判断,所以说是默认开启且无法关闭的。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
结束
好了,今天就到这里了,总结一下,我们看了mybatis跟spring集成的核心配置类,以及顺带跟了一下mybatis的执行过程,看到了一级缓存跟二级缓存的具体实现。