共计 2884 个字符,预计需要花费 8 分钟才能阅读完成。
通过 MyBatis 提供的弱小机制,应用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦挡的办法签名即可。
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
),
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}
),
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}
),
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}
),
})
@Component
@Order(-2)
public class CustomizeInterceptor implements Interceptor {
@Override
public Object plugin(Object target) {return Plugin.wrap(target, this);
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取代理对象
log.info("拦挡:{}", invocation.getMethod().getName());
return invocation.proceed();}
}
- MybatisConfiguration 中源码解析办法
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 MybatisBatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {executor = new MybatisReuseExecutor(this, transaction);
} else {executor = new MybatisSimpleExecutor(this, transaction);
}
if (cacheEnabled) {executor = new MybatisCachingExecutor(executor);
}
// 插件拦截器链
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
- InterceptorChain 中拦截器链条
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {
// 调用
target = interceptor.plugin(target);
}
return target;
}
// 调用接口
default Object plugin(Object target) {return Plugin.wrap(target, this);
}
3. 它又持续调用了 Plugin 的静态方法 wrap
public static Object wrap(Object target, Interceptor interceptor) {
// 获取签名办法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 生成代理对象 动静代理 调用 invoke
return Proxy.newProxyInstance(type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
4. 执行 拦挡
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
// 调用拦挡办法
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);
}
}
- 流程图
最终 Executor 不再是原来的类,而是它的代理类。newStatementHandler 办法和 newResultSetHandler 办法的流程,也差不多,最终也是生成代理类
6. 当 Executor、StatementHandler、ParameterHandler、ResultSetHandler 执行他们本人的办法时,实际上调用他们的代理类 Plugin 中的 invoke 办法
7. 多个 plugins 拦截器代理 后面的代理类被前面的拦截器又代理
套娃行为
所以,前面的将会代理后面的,——越外层的越先执行
8. 多个插件的执行程序曾经明了了,那么插件外面办法的执行程序呢?
query->prepare->setParameters->handleResultSets
这个博客还是讲的很分明的
参考博客:https://blog.csdn.net/qq_18433441/article/details/84036317
正文完