Mybatis插件Plugin

在对数据库操作时,免不了要用到JDBC,但是这部分代码写起来非常的冗长繁琐。于是乎,“懒惰”的人们为了避免做重复相同的事情,持久层框架应运而生。Mybatis便是其中一款优秀的框架,使用非常广泛,免除了我们几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。我们不探讨Mybatis的基本使用,因为从它的官方文档就能获取。本文将从Mybatis Plugin为切入点,从而大致了解一下Mybatis的工作原理。不过一个完整的框架,不是一篇文章就能讲完的,因此本文也只是做到“抛砖引玉”作用,希望能梳理出一个大致的脉络,让大家在此基础上能更清晰地阅读Mybatis源码。

Mybatis Plugin介绍

Plugin即插件,这里我们可以理解成拦截器。这是Mybatis提供的开放功能,它允许我们对SQL执行过程中的某一些点进行拦截调用。因此我们把插件也可以理解成一种扩展,官方文档中提到四个核心接口:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

接口后面将能够拦截的方法全部列举了出来,这里并不直接代入性地介绍它们各自的作用,首先我们看下官方给的使用插件的例子:

@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
// 在配置文件中配置上就行
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
@Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { return invocation.proceed(); } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } } ​ // 在配置文件中配置上就行 <plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}
​
// 在配置文件中配置上就行
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

使用起来还是很简单的,只需要实现Interceptor 接口,然后用注解@Intercepts为它打上需要拦截的方法、类名的标记,然后配置文件丽注册上它即可。

Plugin注册

既然我们声明了一个自定义的插件类,这个类实现了Interceptor 接口,那总该框架要将它应用进去。肯定有一个地方将它注册进去了。

private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// 将解析出来的Interceptor放入到configruation中
configuration.addInterceptor(interceptorInstance);
}
}
}
private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); // 将解析出来的Interceptor放入到configruation中 configuration.addInterceptor(interceptorInstance); } } }
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        // 将解析出来的Interceptor放入到configruation中
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

我们看到,这里将配置文件中的插件节点解析出来放入到configuration配置中。

// Configuration的添加其实就是将插件放入插件链中。
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
// Configuration的添加其实就是将插件放入插件链中。 public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); }
 // Configuration的添加其实就是将插件放入插件链中。
  public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

从它取得名字interceptorChain,我们大致能猜出来插件的链式调用:

public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
// 装载插件
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
public class InterceptorChain { ​ private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); ​ // 装载插件 public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } ​ public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } ​ }
public class InterceptorChain {
​
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
​
  // 装载插件
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
​
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
​
}

接下来,我们通过查找调用关系,看下pluginAll函数在哪里被调用:

public class Configuration {
......
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 装载ParameterHandler插件
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 装载ResultSetHandler插件
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 装载StatementHandler插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。
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);
}
// 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 装载Executor插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
......
}
public class Configuration { ...... ​ public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 装载ParameterHandler插件 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } ​ public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); // 装载ResultSetHandler插件 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } ​ public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 装载StatementHandler插件 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } ​ // 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。 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); } // 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。 if (cacheEnabled) { executor = new CachingExecutor(executor); } // 装载Executor插件 executor = (Executor) interceptorChain.pluginAll(executor); return executor; } ...... }
public class Configuration {
  ......
​
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // 装载ParameterHandler插件
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
​
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    // 装载ResultSetHandler插件
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }
​
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 装载StatementHandler插件
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
​
  // 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。
  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);
    }
    // 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 装载Executor插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
  
  ......
}

在创建上述解释的几种核心类型时候,开始对插件进行了装载。代码还是比较简单的,因此解析.xml配置文件时候对插件进行解析并保存到InterceptorChain中,然后对各个核心接口进行对象创建时,对插件进行装载注入。接下来就是再看下对插件注入的关键代码 Plugin.wrap(target, this):

public class Plugin implements InvocationHandler {
......
public static Object wrap(Object target, Interceptor interceptor) {
// 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
......
}
public class Plugin implements InvocationHandler { ...... public static Object wrap(Object target, Interceptor interceptor) { // 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { // 动态代理 return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } ​ ...... }
public class Plugin implements InvocationHandler {
  ......
    public static Object wrap(Object target, Interceptor interceptor) {
    // 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      // 动态代理
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
​
  ......  
}

熟悉动态代理的同学应该是不陌生的,因此configuration中,创建各个核心接口的方法中其实返回的是一个代理对象,其中已经装载了我们自定义的插件,只等待一个合适机会去触发!接下来我们就可以对还没有介绍的四个核心接口—Executor,ParameterHandler,ResultSetHandler,StatementHandler该出场了。

Executor-执行器

我们都知道,SqlSession是Mybatis对外暴露提供的一个接口,调用它来进行数据库的相关操作。它有一个默认实现DefaultSqlSession,比如当我们查询数据时,实际上它是将请求转交给Executor处理:

public class DefaultSqlSession{
......
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据id找出配置中对应的Mapper。
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();
}
}
......
}
public class DefaultSqlSession{ ...... @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 根据id找出配置中对应的Mapper。 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(); } } ...... }
public class DefaultSqlSession{
  ......
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 根据id找出配置中对应的Mapper。
      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();
    }
  }
  ......
}

其实在executor执行器中也定义了很多基本的数据库操作方法。因此基本上来说,SqlSession其实把相应的请求转交给Executor执行。我们对Executor中想要拦截的方法,其实就是在这里进行执行的,应该来说这一步是整个请求过程中比较开始一步。接下来就简单介绍下它里面的方法:

// 更新操作
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询。还有一个query返回的是游标,此处暂不列出来
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
//批量执行SQL时调用
List<BatchResult> flushStatements() throws SQLException;
//提交事务
void commit(boolean required) throws SQLException;
//回滚事务
void rollback(boolean required) throws SQLException;
//获取事务对象
Transaction getTransaction();
//关闭Executor
void close(boolean forceRollback);
//判断当前Executor是否关闭
boolean isClosed();
// 更新操作 int update(MappedStatement ms, Object parameter) throws SQLException; // 查询。还有一个query返回的是游标,此处暂不列出来 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; //批量执行SQL时调用 List<BatchResult> flushStatements() throws SQLException; //提交事务 void commit(boolean required) throws SQLException; //回滚事务 void rollback(boolean required) throws SQLException; //获取事务对象 Transaction getTransaction(); //关闭Executor void close(boolean forceRollback); //判断当前Executor是否关闭 boolean isClosed();
// 更新操作
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询。还有一个query返回的是游标,此处暂不列出来
 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,  CacheKey cacheKey, BoundSql boundSql) throws SQLException;
//批量执行SQL时调用
 List<BatchResult> flushStatements() throws SQLException;
//提交事务
 void commit(boolean required) throws SQLException;
//回滚事务
 void rollback(boolean required) throws SQLException;
//获取事务对象
 Transaction getTransaction();
//关闭Executor
 void close(boolean forceRollback);
//判断当前Executor是否关闭
 boolean isClosed();

我们可以根据需要对上述的方法进行拦截,定制出属于自己的功能!而对于Executor,MyBatis本身就提供一个公共抽象类来实现一些公共的方法:BaseExecutor。我们还是以查询入手,继续往下探究一下。

public abstract class BaseExecutor{
......
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@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();
// 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
......
}
public abstract class BaseExecutor{ ...... @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); // 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } ​ @SuppressWarnings("unchecked") @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(); // 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } ...... }
public abstract class BaseExecutor{
  ......
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
​
  @SuppressWarnings("unchecked")
  @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();
      // 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
  ......
}

从上面的代码中,我们可以很清楚看到localCache其实就是一个缓存,它内部其实就是利用HashMap来实现的。这个缓存其实就是一级缓存,它将整个SqlSession查询的结果进行短暂的缓存,该缓存会在update或者事务提交、回滚时进行清除。

我们再看下BaseExecutorqueryFromDatabase的简单实现:

public abstract class BaseExecutor{
.......
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 {
// doQuery是一个抽象方法,它由子类实现。
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;
}
......
}
// 子类SimpleExecutor的doQuery方法。
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public abstract class BaseExecutor{ ....... 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 { // doQuery是一个抽象方法,它由子类实现。 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; } ...... } ​ // 子类SimpleExecutor的doQuery方法。 @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
public abstract class BaseExecutor{
  .......
  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 {
        // doQuery是一个抽象方法,它由子类实现。
        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;
    }
  ......
}
​
// 子类SimpleExecutor的doQuery方法。
@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

从数据库中查询还是比较简单的,核心的还是doQuery方法。最终doQuery由各自的子类实现,上面列出来的doQuery的实现,我选用的是默认的SimpleExecutor。从代码中看出来,executor又将查询交给StatementHandler执行。到这里发现又有一个核心接口暴露了出来:StatementHandler

StatmentHandler–Statment处理器

从这个接口的名称我们也能看出,该处理器处理的对象已经开始围绕statment展开,这个就是平时我们写JDBC代码时绕不开的接口,在这里我们看到了它的身影,首先我们把该处理器接口方法简单列举一下:

public interface StatementHandler {
// 从connection中获取statment对象
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 绑定参数
void parameterize(Statement statement)
throws SQLException;
// 批量执行SQL
void batch(Statement statement)
throws SQLException;
// 以下就是query delete update相关操作
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 相关的sql对象,sql中可能包含 “?”
BoundSql getBoundSql();
// ParameterHandler 参数处理器,该接口后续会提及
ParameterHandler getParameterHandler();
}
public interface StatementHandler { // 从connection中获取statment对象 Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; // 绑定参数 void parameterize(Statement statement) throws SQLException; // 批量执行SQL void batch(Statement statement) throws SQLException; // 以下就是query delete update相关操作 int update(Statement statement) throws SQLException; <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; // 相关的sql对象,sql中可能包含 “?” BoundSql getBoundSql(); // ParameterHandler 参数处理器,该接口后续会提及 ParameterHandler getParameterHandler(); }
public interface StatementHandler {
  // 从connection中获取statment对象
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  // 绑定参数
  void parameterize(Statement statement)
      throws SQLException;
  // 批量执行SQL
  void batch(Statement statement)
      throws SQLException;
  // 以下就是query delete update相关操作
  int update(Statement statement)
      throws SQLException;
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  // 相关的sql对象,sql中可能包含 “?” 
  BoundSql getBoundSql();
  // ParameterHandler 参数处理器,该接口后续会提及
  ParameterHandler getParameterHandler();
}

Mybatis默认使用的是RoutingStatementHandler,它实际上是一种策略模式方法,根据MappedStatement的类型来选择具体使用哪种StatmentHandler,其中包括SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler,它们分别对应JDBC中的Statment、PreparedStatementCallableStatement从插件角度来看,我们可以通过拦截该StatmentHandler接口, 来对statment做一些自定义处理。

我们简单看下SimpleStatmentHandler的query方法实现:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
// 结果集映射处理
return resultSetHandler.<E>handleResultSets(statement);
}
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { String sql = boundSql.getSql(); statement.execute(sql); // 结果集映射处理 return resultSetHandler.<E>handleResultSets(statement); }
@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    // 结果集映射处理
    return resultSetHandler.<E>handleResultSets(statement);
  }

上面其实就是调用statment来执行查询请求,接着又碰到了一个核心接口resultSetHandler,用来处理映射结果。因此从这里开始,请求才被发送出去。上面我们提及了下,boundSql包含了需要执行的SQL,但是有可能包含”?”,所以在PreparedStatementHandler中,需要对参数进行处理:

@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

此时我们又看到了一个核心接口parameterHandlerparameterize方法会在执行之前,对SQL中绑定的参数进行处理。

ParameterHandler–参数处理器

public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement ps) throws SQLException; }
public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps)
      throws SQLException;
}

该接口就比较简单,主要就是两个方法对参数处理。如果我们通过插件来拦截这个接口,我们可以对整个参数处理进行自定义的处理。它有一个默认实现DefaultParameterHandler。其中对参数的转换,又涉及到TypeHandlerParameterMapping一些关键类,有兴趣同学可以自己阅读相关源码。

到这里我们可以稍微总结下,对于Executor,Parameterhandler的拦截,此时我们的查询请求依然在预处理的时机,statmentHandler则是真正请求发出的时机,接下来出场的ResultSetHandler则是请求返回之后的处理流程了。

ResultSetHandler–结果集处理器

当我们使用ResultMap或是ResultType配置时,其实就是配置映射规则,ResultSetHandler则会对我们配置的规则将结果集映射成相应的对象。

public interface ResultSetHandler {
// 结果集映射成相应的对象集合
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 结果集映射成游标对象
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 存储过程参数处理
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
public interface ResultSetHandler { // 结果集映射成相应的对象集合 <E> List<E> handleResultSets(Statement stmt) throws SQLException; // 结果集映射成游标对象 <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException; // 存储过程参数处理 void handleOutputParameters(CallableStatement cs) throws SQLException; }
public interface ResultSetHandler {
// 结果集映射成相应的对象集合
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  // 结果集映射成游标对象
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  // 存储过程参数处理
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}

该接口也有一个默认实现:DefaultResultSetHandler。对结果集的映射可以说是Mybatis相对来说比较复杂的一个过程,因为它需要对各种场景结果进行处理,其中还有嵌套映射,延迟加载,游标处理等等一些场景,本片介于篇幅并不打算一一展开,点到为止,同样的有兴趣同学可以自己阅读相关源码。

总结

对于插件我们梳理了它的使用注册过程,紧接着我们对插件拦截的核心接口进行了同样的简单介绍,我们能清楚明白我们拦截的接口具体能做什么,只有明白了这些,我们才好定义自己想要的功能。