全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

插件代理链原理

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;
}

@Signatureargs 必须与目标方法参数类型、数量、顺序完全一致,否则 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 动态代理的原因:

  • ExecutorStatementHandlerParameterHandlerResultSetHandler 都是接口
  • 无需修改目标类结构,代理透明
  • 接口方法固定,签名匹配精确

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

📝 发现内容有误?点击此处直接编辑

← 上一篇 多插件执行顺序
下一篇 → 数据脱敏插件
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库