Mybatis 插件实现原理
org.apache.ibatis.plugin.InterceptorChain
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
- 在初始化时加载xml中配置的拦截器,添加到InterceptorChain的interceptors集合中,
- 在创建
ParameterHandler
,ResultSetHandler
,StatementHandler
,Executor
对象时调用pluginAll(Object target)
方法,遍历Interceptor
执行org.apache.ibatis.plugin.Interceptor#plugin
, - 该方法内部调用
org.apache.ibatis.plugin.Plugin#wrap(Object target, Interceptor interceptor)
解析Interceptor
上定义的@Intercepts
和@Signature
注解来解析需要被拦截的方法,对其使用JDK动态代理,代理类的java.lang.reflect.InvocationHandler
实现即为org.apache.ibatis.plugin.Plugin
,target
做为Plugin的成员变量保存。 - 在遍历interceptors集合创建代理类时,上一个Interceptor返回的target代理会做为下一个Interceptor的目标类。
- 在执行时判断被拦截的方法是否需要执行
interceptor
,需要则执行。不需要则直接调用target目标类的方法。
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
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) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@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);
}
}
......
}
Mybatis 插件实现弊端
在循环调用interceptor.plugin(target);
时, 如果存在多个Interceptor
拦截的即使是同一个接口(ParameterHandler
, ResultSetHandler
, StatementHandler
, Executor
其中一个),因为每次传入的是上一个target = interceptor.plugin(target)
返回的对象,会导致目标类被代理多次。这实际上形成了一个动态代理的拦截器链
ProxyA(Hnadler)
--> ProxyB(Hnadler)
--> ProxyC(Hnadler)
--> ProxyD(Hnadler)
......
- 首先代理类的方法调用在性能上不如直接调用目标类的方法高效,并且代理越多性能越差。@See PR
- 多次代理同一个类,会导致对象实例结构复杂,不利于分析插件的执行逻辑。
Mybatis 插件实现优化
com.bruce.mybatis.plugin.optimize.OptimizedPlugin
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.reflection.ExceptionUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author bruce lwl 2023/4/6 23:43
* @see Plugin
*/
public class OptimizedPlugin implements InvocationHandler {
private final Object target;
private final Map<Method, List<Interceptor>> methodInterceptors;
public OptimizedPlugin(Object target) {
this.target = target;
this.methodInterceptors = new HashMap<>();
}
public void addMethodInterceptors(Map<Method, List<Interceptor>> interceptors) {
for (Map.Entry<Method, List<Interceptor>> entry : interceptors.entrySet()) {
List<Interceptor> interceptors1 = methodInterceptors.computeIfAbsent(entry.getKey(), key -> new ArrayList<>());
interceptors1.addAll(entry.getValue());
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
List<Interceptor> interceptors = methodInterceptors.get(method);
if (interceptors != null) {
return new InvocationChain(interceptors, target, method, args).proceed();
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
/**
* @see Plugin#wrap(Object, Interceptor)
*/
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) {
if (Proxy.isProxyClass(type) && Proxy.getInvocationHandler(target) instanceof OptimizedPlugin) {
OptimizedPlugin optimizedPlugin = (OptimizedPlugin) Proxy.getInvocationHandler(target);
Map<Method, List<Interceptor>> methodInterceptors = getMethodInterceptors(interfaces, signatureMap, interceptor);
optimizedPlugin.addMethodInterceptors(methodInterceptors);
return target;
}
OptimizedPlugin optimizedPlugin = new OptimizedPlugin(target);
Map<Method, List<Interceptor>> methodInterceptors = getMethodInterceptors(interfaces, signatureMap, interceptor);
optimizedPlugin.addMethodInterceptors(methodInterceptors);
return Proxy.newProxyInstance(type.getClassLoader(), interfaces, optimizedPlugin);
}
return target;
}
/**
* @param interfaces 对正在对哪些接口代理
* @param signatureMap 指定哪些接口有哪些方法是被拦截的
* @param interceptor Interceptor实例对象, 由用户定义拦截时的具体逻辑
* @return
*/
private static Map<Method, List<Interceptor>> getMethodInterceptors(Class<?>[] interfaces, Map<Class<?>, Set<Method>> signatureMap, Interceptor interceptor) {
HashMap<Method, List<Interceptor>> methodInterceptors = new HashMap<>();
for (Class<?> anInterface : interfaces) {
Set<Method> methods = signatureMap.get(anInterface);
for (Method method : methods) {
methodInterceptors.computeIfAbsent(method, (key) -> new ArrayList<>()).add(interceptor);
}
}
return methodInterceptors;
}
/**
* 返回目标类,哪些方法是被拦截的
*/
@SuppressWarnings("unchecked")
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
try {
Method getSignatureMapMethod = Plugin.class.getDeclaredMethod("getSignatureMap", Interceptor.class);
getSignatureMapMethod.setAccessible(true);
return (Map<Class<?>, Set<Method>>) getSignatureMapMethod.invoke(Plugin.class, interceptor);
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException();
}
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
try {
Method getAllInterfacesMethod = Plugin.class.getDeclaredMethod("getAllInterfaces", Class.class, Map.class);
getAllInterfacesMethod.setAccessible(true);
return (Class<?>[]) getAllInterfacesMethod.invoke(Plugin.class, type, signatureMap);
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException();
}
}
com.bruce.mybatis.plugin.optimize.OptimizedInterceptor
public interface OptimizedInterceptor extends Interceptor {
@Override
default Object plugin(Object target) {
return OptimizedPlugin.wrap(target, this);
}
}
InvocationList
package org.apache.ibatis.plugin;
import org.apache.ibatis.reflection.ExceptionUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class InvocationList extends Invocation {
private final List<Interceptor> interceptors;
int index = 0;
public InvocationList(List<Interceptor> interceptors, Object target, Method method, Object[] args) {
super(target, method, args);
this.interceptors = interceptors;
index = interceptors.size();
}
@Override
public Object proceed() throws InvocationTargetException, IllegalAccessException {
if ((index--) == 0) {
return super.proceed();
}
Interceptor interceptor = interceptors.get(index);
Object result = null;
try {
result = interceptor.intercept(this);
} catch (Throwable throwable) {
try {
throw ExceptionUtil.unwrapThrowable(throwable);
} catch (Throwable e) {
e.printStackTrace();
}
}
return result;
}
}
优化后效果