本次我们学习mybatis的插件,重点是理解他和动态代理的关系
上节我们已经详细的了解动态代理;主要是通过定义接口,通过
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h)
方法,创建一个接口的代理。interfaces 表示代理也需要遵守的协议,还有h是实现
InvocationHandler协议的实例,其中有一个方法invoke,也是逻辑增强或者类功能增强的地方。具体请参考:使用JDK API实现动态代理和源码分析
下面我们正式分析mybatis的插件
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class TenantInterceptor implements Interceptor {
我们最常见的插件应该就是这样的,首先实现Interceptor方法接口;
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
我们关注上面上面这两个方法。
首先我们给出答案,intercept方式是逻辑增强的方法,plugin是创建代理的方法。
但是直接看好像和我们动态代理没有关系
我们从 return Plugin.wrap(target, this);
方法开始,也就是Interceptor 的第二个方法,在wrap方法中创建了代理
public static Object wrap(Object target, Interceptor interceptor) { // 1
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance( // 2
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
intercept
有增强逻辑,例如修改SQL,分页插件等等,是我们生成代理需要的。Plugin
,Plugin
类一定实现InvocationHandler接口。还是在Plugin
中的invoke
方法,也就是InvocationHandler
接口方法。
@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));//1
}
return method.invoke(target, args);//2
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
interceptor
方法生效了,也就是逻辑增强在Mybatis中重要的接口Executor
和StatementHandler
,其中的方法逐层调用,都是在为执行SQL配置环境,其中StatementHandler就是。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
上面是通过configuration配置类,创建StatementHandler
,下面是具体的代码。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler c= new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 1
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler; // 2
}
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target); // 1
}
return target;
}
pluginAll
方法,具体的增加就是创建调用我们编写插件的plugin方法,创建代理对象,例如public Object plugin(Object target) {
return Plugin.wrap(target,this); // 创建实例
}
插件就是在调用Mapper方法时,通过动态代理对Mybatis的某些接口增强,通过Interceptor的 @Signature注解指定需要增强的接口方法.
还有很多细节,欢迎大家讨论学习