SSM-Mybatis-插件-插件的代理和反射设计
插件用的是责任链模式,而Mybatis的责任链模式是通过interceptorChain
executor=(Executor) interceptorChain.pluginAll(executor);
pluginAll方法代码:
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)var2.next();
}
return target;
}
plugin方法是生成代理对象的方法,从Configuration对象中取出插件的
自己编写代理类工作量很大,Mybatis提供一个常用工具类,用来生成代理对象,它是Plugin,这个类实现了InvocationHandler接口,采用JDK动态代理,该类有两个重要方法:
public class Plugin implements InvocationHandler {
...
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
...
}
wrap:方法生成这个对象的动态代理对象
invoke:使用这个类为插件生成代理对象,那么代理对象在调用方法时就会进入invoke方法中。在invoke方法中,如果存在签名的拦截方法,插件的intercept方法就会在这里调用,然后返回结果。如果不存在签名方法,那么将直接反射调度要执行的方法
Mybatis把被代理对象,反射方法及参数,都传递给了Invocation类的结构方法,用以生成一个Invocation类对象,Invocation类中有一个proceed()方法,
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return this.target;
}
public Method getMethod() {
return this.method;
}
public Object[] getArgs() {
return this.args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return this.method.invoke(this.target, this.args);
}
}
从源码看到,proceed方法通过反射的方法调度被代理对象的真实方法。
大部分情况下,Mybatis的Plugin类生成代理对象足够我们使用