原理
mybatis插件类型
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
那么 这些插件的执行顺序是如何的呢?
引入mybatis插件
maven依赖(略)
详见: gitee
配置插件
<!-- 配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- ..... -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
自定义插件
插件的方法内容很简单,即打印当前方法的签名:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class })
})
@Slf4j
public class Executor_Interceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("---------------------"+this.getClass().getSimpleName() +"::"+invocation.getMethod().getName());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
同理,以此注入如下的插件:
// Executor2_Interceptor ,Executor3_Interceptor 同 Executor_Interceptor
//ParameterHandler 监听setParameters方法
@Intercepts({
@Signature( type= ParameterHandler.class,method = "setParameters", args = {PreparedStatement.class})
})
public class ParameterHandler_Interceptor implements Interceptor {}
//ResultSetHandler 监听 handleResultSets 方法
@Intercepts({
@Signature( type= ResultSetHandler.class,method = "handleResultSets", args = {Statement.class}),
})
public class ResultSetHandler_Interceptor implements Interceptor {}
//StatementHandler 监听 prepare和query方法
@Intercepts({
@Signature( type= StatementHandler.class,method = "prepare", args = {Connection.class,Integer.class}),
@Signature( type= StatementHandler.class,method = "query", args = {Statement.class,ResultHandler.class})
})
public class StatementHandler_Interceptor implements Interceptor {}
同类型插件的执行顺序
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor2_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor3_Interceptor"></plugin>
</plugin>
</plugins>
</configuration>
执行查询方法–日志
结论
相同类型的plugins:按照 注册的
逆序,执行。
不同类型插件的执行顺序
mybatis-config.xml
<plugins>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.ParameterHandler_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.ResultSetHandler_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.StatementHandler_Interceptor"></plugin>
</plugins>
执行查询方法–日志
猜测
不同类型的plugins:按照 是否注册的顺序
影响呢?
随机顺序-不同plugin
<plugins>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.ResultSetHandler_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.ParameterHandler_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.StatementHandler_Interceptor"></plugin>
<plugin interceptor="cn.jhs.framework.mybatis.plugins.Executor_Interceptor"></plugin>
</plugins>
此时的执行结果,跟上述结果相同
结论
总体顺序Executor
-> StatementHandler
->ParameterHandler
-> ResultSetHandler
,
但是部分-方法的顺序,不受上述规则的控制。如日志中的StatementHandler.prepare()和StatementHandler.query()
源码分析
我们都知道,XXMapper#selectXXX()
; 转换为 DefaultSqlSession#selectList()
; 下面从DefaultSqlSession入手,分析,上述插件的执行顺序的原理。
构建DefaultSqlSession — 初始化
--DefaultSqlSessionFactory#openSessionFromDataSource()
----configuration#newExecutor(); //1. 创建 executor
------executor = new SimpleExecutor(); //1.1 创建SimpleExecutor
------executor = new CachingExecutor(executor); //1.2 包装成: CachingExecutor
------executor = (Executor) interceptorChain.pluginAll(executor); //1.3 使用 interceptorChain.代理executor
----------for-each -> interceptor.plugin(target); //1.3.1 内部调用: Plugin.wrap(target, this);
----------Plugin#wrap(target, this); //1.3.1.1 生成executor代理对象
public static Object wrap(Object target, Interceptor interceptor) {
//a. 获取代理interceptor所有签名的<接口,Set<接口methods>>
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
//b. target = CachingExecutor;
Class<?> type = target.getClass();
//c. type(CachingExecutor.class)与interceptor签名接口 交集;
// 此时CachingExecutor实现了Executor接口, 故只有@Signature中包含Executor接口的interceptor,会生成代理对象;
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//d. 如果存在交集, 则生成代理对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
//e. 如果不存在交集,则返回原始对象
return target;
}
------返回一个可能存在多重Plugin..warp的(CachingExecutor(SimpleExecutor)) 对象;
-----new DefaultSqlSession(configuration, executor, autoCommit); // 2. 构造DefaultSqlSession对象
此时构建出一个DefaultSqlSession对象,它的内容如下:
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
private Executor executor; // 仅包含 plugins中,@Signature中包含Executor接口的interceptor
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}
}
执行查询
接上一节内容,所有的查询都会转换为executor.query
, 此时的executor为@Signature中包含Executor接口的interceptor生成的Plugin代理对象
。
1. 执行Plugin<Executor.class>
Plugin执行代理方法的逻辑如下。 此时的Plugin代理的是Executor
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set<Method> methods = signatureMap.get(method.getDeclaringClass()); //1.1 查看当前Plugin支持的方法类型set
if (methods != null && methods.contains(method)) { //1.2 检验执行mapper方法类型,与当前Plugin设置的方法 是否一致
return interceptor.intercept(new Invocation(target, method, args)); //1.3. 链式调用*执行插件interceptor.intercept方法
}
return method.invoke(target, args); //1.4 执行原始对象 CachingExecutor();
}
2. 执行CachingExecutor#query()
上一步执行完成之后,最终执行真实对象查询方法CachingExecutor#query
.
-------delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//delegate = SimpleExecutor
//SimpleExecutor extends BaseExecutor
//delegate.query() 执行 BaseExecutor#query(), 而BaseExecutor内部调用抽象方法doQuery,由 SimpleExecutor实现
---------SimpleExecutor#doQuery(); // 2.1
-----------StatementHandler handler = configuration.newStatementHandler() // 2.1.1
-------------StatementHandler statementHandler = new RoutingStatementHandler(); //2.1.1.1 构造 RoutingStatementHandler
---------------delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); //2.1.1.1.1 内部delegate
//delegate 构造内容
public class PreparedStatementHandler extends BaseStatementHandler {
protected BaseStatementHandler() {
this.parameterHandler = configuration.newParameterHandler(); //a. 内部调用创建 ParameterHandler
this.resultSetHandler = configuration.newResultSetHandler(); //b. 内部调用创建 ResultSetHandler
}
}
-------------statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); //2.1.1.2 最终通过Plugin.warp(=RoutingStatementHandler);它的delegate类型为PreparedStatementHandler
-----------Statement stmt = prepareStatement(handler, ms.getStatementLog());// 2.1.2
-------------Connection connection = getConnection(statementLog); // 2.1.2.1 获取数据库连接
-------------stmt = handler.prepare(connection, transaction.getTimeout()); // 2.1.2.2 PreparedStatementHandler->BaseStatementHandler#prepare ;
-------------handler.parameterize(stmt); // 2.1.2.3 PreparedStatementHandler#parameterize ;
-----------return handler.<E>query(stmt, resultHandler);// 2.1.3
------------PreparedStatementHandler#query()
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //2.1.3.1 执行ps
return resultSetHandler.<E> handleResultSets(ps); //2.1.3.2 resultSetHandler 渲染执行结果
}
关键类图
执行顺序
-
handler.prepare
: StatementHandler -
handler.parameterize
: StatementHandler + ParameterHandler -
handler.<E>query
: StatementHandler + ParameterHandler -
resultSetHandler.<E> handleResultSets(ps)
: ResultSetHandler