通过 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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。