介绍
该笔记是在学习拉勾教育 Java 高薪训练营后,结合课程和老师的视频,自己跟踪源码后做的笔记。
Mybatis 插件运行原理
Mybatis 中的四大对象 Executor、ParameterHandler、ResultSetHandler、StatementHandler 都是代理对象。
如下代码,在创建时会调用 interceptorChain.pluginAll() 方法,遍历调用 plugin 方法判断是否返回目标对象本身,还是进行拦截返回代理对象。 如果拦截返回代理对象的话,之后调用时,会调用代理对象的拦截方法 intercept 进行拦截增强,从而实现调用插件。这与 RPC 客户端在调用接口原理类似,同样会对接口方法进行拦截,在拦截方法中实现一套远程调用逻辑。
/**
* 拦截器链
*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 创建 ParameterHandler 对象
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// 创建 ParameterHandler 对象
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 应用插件
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
// 创建 ResultSetHandler 对象
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
// 创建 DefaultResultSetHandler 对象
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 应用插件
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
// 创建 StatementHandler 对象
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建 RoutingStatementHandler 对象
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 应用插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
/**
* 创建 Executor 对象
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// ...
// 应用插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
自定义插件
Mybatis 在初始化时,将该自定义插件添加到拦截器链 InterceptorChain,当创建对象,比如 newExecutor 时会调用 pluginAll 方法。
这里的目标对象 target 为 newExecutor,会遍历拦截链,找到针对 newExecutor 的自定义插件,返回其代理对象。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
实现 Executor 的自定义插件(代理对象)
需要实现拦截器接口,使用注解 @Intercepts 来表示针对哪个对象进行拦截(返回其代理对象),以及该对象的方法和对应参数。如下会对 Executor 的 query 方法进行拦截,在调用该方法时,先调用代理对象的 intercept 方法。
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class MyInterceptor implements Interceptor {
@Override
Object intercept(Invocation invocation) throws Throwable {}
@Override
Object plugin(Object target) {}
@Override
void setProperties(Properties properties) {}
}