mybatis的plugin.wrap方法通过jdk动态代理实现条件代理,仅当目标对象匹配@intercepts声明的接口类型(如executor)时才创建代理,否则直返原对象;其内部invocationhandler在invoke中先执行intercept逻辑,再通过invocation.proceed()触发责任链下一级或原方法。

MyBatis 的 Plugin.wrap 方法通过 JDK 动态代理实现拦截逻辑,核心是判断目标对象是否实现了插件声明的接口(如 Executor、StatementHandler 等),仅对匹配的对象创建代理,避免无谓代理开销。
wrap 方法的关键逻辑:条件代理,非无脑代理
该方法不是对任意对象都套一层代理,而是先检查目标对象是否“属于”当前插件关心的类型:
- 调用
plugin instanceof T(T 是插件中intercept方法签名里声明的接口类型)判断插件本身是否已实现该接口(用于嵌套代理场景) - 调用
Plugin.isPlugin(Object target)判断目标对象是否实现了插件声明的接口(例如插件写了@Intercepts(@Signature(type = Executor.class, ...)),就检查target instanceof Executor) - 只有满足条件时才调用
Proxy.newProxyInstance(...)创建代理;否则直接返回原对象
代理处理器:InvocationHandler 实际执行拦截
真正干活的是内部定义的匿名 InvocationHandler,它在 invoke 方法中:
- 先尝试调用用户自定义的
interceptor.intercept(Invocation)—— 这就是你写插件逻辑的地方 - 若未被拦截(比如
intercept返回了null或未调用proceed()),则 fallback 到原方法调用:method.invoke(target, args) - 注意:MyBatis 自带的
Invocation类封装了 target、method、args,方便你在intercept中选择性放行或修改参数/结果
为什么需要 isPlugin 和 wrap 配合?—— 支持插件链式叠加
多个插件可能同时作用于同一个对象(比如都拦截 Executor)。这时:
- 第一个插件调用
wrap(executor)→ 返回代理 A - 第二个插件再对代理 A 调用
wrap(proxyA)→ 因为proxyA instanceof Executor仍为 true,所以继续代理,得到代理 B - 最终执行时,调用链是:B → A → 原 executor,形成责任链
-
isPlugin保证了即使传入的是已有代理对象,只要它“看起来像”目标类型,就继续包装,从而支持多层拦截
实际使用中的典型误区
开发者常误以为 wrap 会自动代理所有相关对象,但其实:
- 它只代理
@Signature.type指定的接口类型,不会递归代理其字段或依赖对象(比如Executor内部的Transaction不会被自动代理) - 代理对象本身也实现了被代理接口(靠 JDK Proxy 机制),所以能继续被后续插件识别和包装
- 如果你在
intercept中返回了新对象(比如重写了 SQL),要确保该对象仍符合预期类型,否则下游插件可能跳过它
不复杂但容易忽略:wrap 是 MyBatis 插件机制的入口开关,它的条件判断和代理构造共同支撑了轻量、可叠加、类型安全的拦截能力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











