最近在阅读Mybatis源码,一步步边调试边阅读时发现,执行器executor调用query方法时,一会执行CachingExecutor里的query,一会执行BaseExecutor里的query,被绕晕了,于是准备梳理一下他们的联系。
一、
首先需要明确:BaseExecutor、CachingExecutor均为接口Executor的实现类,SimpleExecutor是BaseExecutor的子类。
二、
然后再看Configuration类中执行器的创建过程:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//现根据执行器样式生成基本的执行器,后再通过装饰器模式、责任链模式修改
if (ExecutorType.BATCH == executorType) {//批量执行器
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {//复用执行器
executor = new ReuseExecutor(this, transaction);
} else {//简单执行器
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
//判断是否启用缓存(对应xml中的配置),如果启用,重新封装成缓存执行器
//装饰器设计模式,允许向一个现有的对象添加新的功能,同时又不改变其结构
executor = new CachingExecutor(executor);
}
//责任链设计模式,按照xml中配置的拦截器(可能有多个),依次对执行器设置
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
创建执行器对象一共分为三步:
第一步:根据指定的执行器类型,生成一个批量执行器BatchExecutor或复用执行器ReuseExecutor或简单执行器SimpleExecutor(三者是并列关系,本文以简单执行器SimpleExecutor为例)
第二步:根据xml中设置的是否开启二级缓存,决定是否将第一步获得的执行器进一步包装为缓存执行器CachingExecutor(装饰器设计模式,本文假设开启二级缓存)
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="ture"/>
</settings>
包装时调用 CachingExecutor的构造方法,第一步中获得的执行器成为CachingExecutor的成员变量。
private final Executor delegate;
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
...
}
第三步:根据xml中设置的拦截器,对第二步获得的执行器进一步包装(责任链设计模式)
第三步中将第二步的CachingExecutor作为Object对象传递,没有其他类的参与,因此第三步结束后返回的Executor接口指向的还是CachingExecutor这个实现类(为什么用Object传递最后类型不变,见最后测试)。
三、
在通过动态代理调用时(以查询select为例):
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
executor.query:接口executor调用其指向的实现类的query方法,上文已知,该接口指向实现类CachingExecutor,因此会调用CachingExecutor中的query,下面是CachingExecutor中的query方法:
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//SQL语句
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//null为未打开缓存
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);
}
上方源码中能够看出,query调用流程如下:
第一步:在CachingExecutor中前一个query先被调用后,在方法中再调用后一个query
第二步:后一个query中又有delegate.query,CachingExecutor在构造时,已经将delegate指向了SimpleExecutor对象,因此会转而调用SimpleExecutor中的query
第三步:但是在SimpleExecutor中没有直接写query,而是它的父类BaseExecutor实现了该方法,因此转而调用BaseExecutor中的query(即一开始定位的是SimpleExecutor中的query,但没找到该方法,去父类中找,在父类中找到了该方法),源码如下:
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
@Override
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;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
第四步:BaseExecutor中的query调用queryFromDatabase方法,queryFromDatabase调用doQuery,doQuery是抽象方法,去子类中找实现(此时的子类对应的是SimpleExecutor)
第五步:调用SimpleExecutor的doQuery方法
这就是为什么在调用query方法时,会在几个Executor接口实现类中绕来绕去,以上是对调用过程的解释。
MyBatis博大精深!万物互联!
注:
1、如果一开始xml中没有设置二级缓存,就不会经过CachingExecutor的包装,那么最开始的Executor接口会指向SimpleExecutor,在调用query时会直接在SimpleExecutor中找query方法。
2、BatchExecutor、ReuseExecutor、SimpleExecutor三者并列,它们的父类是BaseExecutor,公共的方法都在父类中实现了,父类中的抽象方法(如doQuery)每个子类各自的实现方法不同,子类重写父类的抽象方法即可,公共方法不用重复再写(这就是模板设计模式)
小测试:写了个Demo模拟几个执行器的关系(按我的理解自己写的,不一定合理)
TestInterface接口(对应Executor接口):
public interface TestInterface {
void show();
}
Simple实现类(对应SimpleExecutor):
public class Simple implements TestInterface{
@Override
public void show() {
System.out.println("Simple---");
}
}
Caching实现类(对应CachingExecutor):
public class Caching implements TestInterface{
TestInterface testInterface;
public Caching(TestInterface testInterface) {
this.testInterface = testInterface;
}
@Override
public void show() {
System.out.println("Caching---");
testInterface.show();
}
}
Plugin类(对应拦截器责任链设计模式那段,以Object传递对象)
public class Plugin {
Object test(Object target){
return target;
}
}
测试:
public class App {
public static void main(String[] args) {
TestInterface testInterface = new Simple();
testInterface = new Caching(testInterface);
testInterface = (TestInterface) new Plugin().test(testInterface);
testInterface.show();
}
}
输出为:
Caching---
Simple---