
MyBatis 的插件链是通过 JDK 动态代理 + `Interceptor` 接口 + `Plugin.wrap()` 机制串联执行的,核心在于“层层包装”:每个插件对目标对象(如 `Executor`、`StatementHandler`)创建一层代理,后注册的插件代理外层,先注册的在内层,调用时按“外→内→目标→内→外”顺序执行。
插件注册顺序决定代理嵌套结构
MyBatis 在初始化时遍历 `configuration.getInterceptors()`,对每个 `Interceptor` 调用 `Plugin.wrap(target, interceptor)`。该方法返回一个 `InvocationHandler` 代理对象,而这个代理对象又作为下一个插件的 `target` 被再次包装。
- 假设注册顺序为:
LogInterceptor→CacheInterceptor→SlowSqlInterceptor - 最终得到的代理链是:
SlowSqlInterceptor代理 → 包裹CacheInterceptor代理 → 包裹LogInterceptor代理 → 包裹原始 `Executor` - 执行时,`slowSqlPlugin.invoke()` 先触发,内部调用 `cachePlugin.invoke()`,再调用 `logPlugin.invoke()`,最后才到真实 `executor.update()`
`Plugin.wrap()` 是代理生成的关键入口
它不是无条件代理,而是先调用 `interceptor.plugin(target)` 让插件自己决定是否代理该 target 类型(比如只拦截 `StatementHandler`,就对 `Executor` 返回原对象);若需代理,则用 `Proxy.newProxyInstance()` 创建 JDK 代理,绑定 `Plugin` 实例作为 `InvocationHandler`。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- `Plugin` 类本身实现了 `InvocationHandler`,其 `invoke()` 方法会调用 `interceptor.intercept(new Invocation(target, method, args))`
- 注意:`interceptor.intercept()` 中若想继续执行后续逻辑(即调用被代理对象的方法),必须显式调用 `invocation.proceed()` —— 这正是链式传递的核心
- 不调用 `proceed()` 就会中断链路(可用于熔断或跳过执行)
拦截点必须匹配 `@Intercepts` 注解声明
每个 `Interceptor` 需用 `@Intercepts` 声明要拦截的目标类型、方法名和参数签名。`Plugin` 在 `wrap()` 前会通过 `Plugin.isPluginApplicable()` 判断当前 `target` 是否匹配任一 `@Signature`。
立即学习“Java免费学习笔记(深入)”;
- 例如:`@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})`
- 只有 `target instanceof Executor` 且方法名为 `update`、参数类型匹配时,`wrap()` 才真正生成代理;否则直接返回原对象
- 因此多个插件可分别作用于不同组件(如一个拦 `Executor`,一个拦 `ParameterHandler`),互不影响
执行流程:代理调用 → intercept() → proceed() → 下一层代理
以 `executor.update(ms, param)` 调用为例:
- 最外层代理(如 `SlowSqlInterceptor`)的 `invoke()` 被触发
- 进入 `slowSql.intercept(new Invocation(executorProxy, update, ...))`
- 其中 `invocation.proceed()` 实际调用的是内层代理(`CacheInterceptor`)的 `invoke()`
- 以此类推,直到最内层代理调用 `invocation.proceed()` → 真实 `Executor` 的 `update()` 方法
- 返回时逐层回调,每层可在 `intercept()` 中对返回值做增强(如缓存结果、记录耗时)

















