插件代理链原理
MyBatis 插件体系基于 JDK 动态代理 + 责任链模式构建,通过 Plugin.wrap() 逐层包装目标对象,形成代理链。
代理链构建入口
四大核心对象在创建时统一经过 InterceptorChain.pluginAll() 处理:
Java
// Configuration.java
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
Executor executor = createExecutor(transaction, executorType);
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// ★ 插件代理链构建入口
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
pluginAll 遍历所有注册拦截器,逐层包装:
Java
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;
}
}
Plugin.wrap() 代理创建
核心源码
Java
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
public static Object wrap(Object target, Interceptor interceptor) {
// 1. 解析 @Intercepts/@Signature 注解,构建签名映射
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 2. 获取目标对象实现的所有接口
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
// 3. 仅当有匹配接口时才创建代理
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target; // 不匹配,返回原对象
}
}
getSignatureMap 解析
Java
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts intercepts = interceptor.getClass().getAnnotation(Intercepts.class);
if (intercepts == null) {
throw new PluginException("No @Intercepts annotation found on " + interceptor.getClass().getName());
}
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : intercepts.value()) {
// 通过 type + method + args 定位唯一方法
Method method = sig.type().getMethod(sig.method(), sig.args());
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
methods.add(method);
}
return signatureMap;
}
@Signature的args必须与目标方法参数类型、数量、顺序完全一致,否则getMethod()抛异常。
getAllInterfaces 接口过滤
Java
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> ifc : type.getInterfaces()) {
// 仅保留 signatureMap 中声明的接口
if (signatureMap.containsKey(ifc)) {
interfaces.add(ifc);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class[0]);
}
仅当目标对象实现了 @Signature 声明的接口类型时,才为其创建代理,否则直接返回原对象(零侵入)。
代理链执行流程
Plugin.invoke() 路由分发
Java
@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);
}
}
Invocation.proceed() 链式传递
Java
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 proceed() throws InvocationTargetException, IllegalAccessException {
// 调用下一层代理或原始对象的方法
return method.invoke(target, args);
}
}
proceed() 调用的是当前代理层包装的 target,即下一层代理或原始对象。
代理链架构
多层代理嵌套结构
Java
假设注册了 3 个插件,均拦截 Executor.query:
ProxyA(InterceptorA)
└── target: ProxyB(InterceptorB)
└── target: ProxyC(InterceptorC)
└── target: CachingExecutor
└── target: SimpleExecutor (原始)
调用链路:
Java
Executor.query()
→ ProxyA.invoke() → InterceptorA.intercept()
→ invocation.proceed()
→ ProxyB.invoke() → InterceptorB.intercept()
→ invocation.proceed()
→ ProxyC.invoke() → InterceptorC.intercept()
→ invocation.proceed()
→ CachingExecutor.query()
→ SimpleExecutor.query() ← 原始方法
← InterceptorC 后置处理
← ProxyC 返回
← InterceptorB 后置处理
← ProxyB 返回
← InterceptorA 后置处理
← ProxyA 返回
ASCII 执行流程图
Java
┌──────────────────────────────────────────────┐
│ Executor.query() │
└──────────────┬───────────────────────────────┘
│
┌──────────────▼───────────────────────────────┐
│ PluginA.invoke() [外层代理] │
│ → InterceptorA.intercept() [前置处理] │
│ → invocation.proceed() │
└──────────────┬───────────────────────────────┘
│
┌──────────────▼───────────────────────────────┐
│ PluginB.invoke() [中层代理] │
│ → InterceptorB.intercept() [前置处理] │
│ → invocation.proceed() │
└──────────────┬───────────────────────────────┐
│
┌──────────────▼───────────────────────────────┐│
│ PluginC.invoke() [内层代理] ││
│ → InterceptorC.intercept() [前置处理] ││
│ → invocation.proceed() ││
└──────────────┬───────────────────────────────┘│
│ │
┌──────────────▼───────────────────────────────┐│
│ CachingExecutor.query() [装饰器] ││
│ → SimpleExecutor.query() [原始执行] ││
└──────────────┬───────────────────────────────┘│
│ │
┌──────────────▼───────────────────────────────┐│
│ ← InterceptorC 后置处理 ←───────────────────┘│
└──────────────┬───────────────────────────────┘
│
┌──────────────▼───────────────────────────────┐
│ ← InterceptorB 后置处理 ←───────────────────┘
└──────────────┬───────────────────────────────┘
│
┌──────────────▼───────────────────────────────┐
│ ← InterceptorA 后置处理 ←───────────────────┘
└──────────────┬───────────────────────────────┘
│
返回结果
JDK 动态代理 vs CGLIB 对比
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 实现目标接口 | 继承目标类生成子类 |
| 目标要求 | 必须有接口 | 类不能是 final |
| 性能 | 反射调用,略慢 | 字节码生成,较快 |
| 方法拦截 | 仅拦截接口方法 | 可拦截所有非 final 方法 |
| MyBatis 选择 | 四大核心对象均有接口 | 未采用 |
MyBatis 选择 JDK 动态代理的原因:
Executor、StatementHandler、ParameterHandler、ResultSetHandler都是接口- 无需修改目标类结构,代理透明
- 接口方法固定,签名匹配精确
Interceptor.plugin() 方法
text
// Interceptor 接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {}
}
plugin() 默认实现调用 Plugin.wrap(),也可自定义:
text
// 自定义 plugin 方法,跳过不相关目标
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target; // 非目标类型,不创建代理
}
自定义
plugin()可在创建代理前做类型判断,避免无效的代理实例。
代理链异常传播
text
@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);
}
}
- 任何一层拦截器抛异常,中断整条链,异常向上传播
ExceptionUtil.unwrapThrowable()剥离InvocationTargetException包装,保留原始异常
要点总结
- 代理链构建入口为
InterceptorChain.pluginAll(),遍历所有拦截器逐层包装 Plugin.wrap()解析@Signature注解,仅当目标实现匹配接口时才创建 JDK 动态代理Plugin.invoke()根据方法签名匹配决定是否执行拦截逻辑,否则直接调用原方法Invocation.proceed()调用下一层代理或原始对象,形成链式传递- 多层代理嵌套执行顺序为:外层前置 → 内层前置 → 原始方法 → 内层后置 → 外层后置
- MyBatis 选择 JDK 动态代理而非 CGLIB,因为四大拦截对象均为接口,代理透明且精确
- 自定义
plugin()方法可在创建代理前做类型过滤,避免无效代理实例
存放路径:D:\git2\jwdev\articles\MYBATIS\专家\插件开发高级应用\插件代理链原理.md
📝 发现内容有误?点击此处直接编辑