proceed()仅放行并交还控制权给目标方法,不执行拦截逻辑;拦截逻辑须写在proceed()前后:前置可改参、日志、SQL解析,后置可加工结果、统计耗时;不调用proceed()即终止拦截,实现条件放行。

proceed() 方法本身不执行拦截逻辑,它只是放行——把控制权交还给被拦截的目标方法(如 Executor.query()、StatementHandler.prepare() 等),让原始流程继续执行。真正的拦截逻辑写在 proceed() 调用的前后。
拦截逻辑写在 proceed() 前后
MyBatis 插件基于 JDK 动态代理,Invocation 封装了目标对象、方法和参数。调用 proceed() 相当于执行 Method.invoke(target, args)。你要做的,是在它之前做预处理(如改参、记录日志、修改 SQL),在它之后做后置处理(如结果包装、耗时统计、脱敏)。
- ✅ 前置拦截:在
proceed()之前操作args或检查上下文(如判断是否为 SELECT 语句) - ✅ 后置拦截:用
Object result = invocation.proceed();获取原方法返回值,再加工或校验 - ❌ 不要“重写”
proceed()—— 它是Invocation内部实现,不可覆盖
典型拦截模式示例
比如想在 SQL 执行前打印完整 SQL 和参数:
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
// ✅ 前置:获取 StatementHandler,解析 BoundSql
if (invocation.getTarget() instanceof StatementHandler) {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
System.out.println("SQL: " + boundSql.getSql());
System.out.println("Params: " + boundSql.getParameterObject());
}
// ⏭️ 放行:执行原始逻辑(如 prepare、parameterize、query)
Object result = invocation.proceed(); // ← 这里才真正触发 JDBC 操作
// ✅ 后置:对查询结果做处理(如统一加字段、过滤敏感数据)
if (result instanceof List && !((List) result).isEmpty()) {
// 自定义处理...
}
return result;
}
注意 proceed() 的执行时机和风险
proceed() 一旦调用,就进入 MyBatis 内部链路,可能触发数据库 I/O、事务管理、缓存操作等。因此:
立即学习“Java免费学习笔记(深入)”;
- 确保前置逻辑不抛异常,否则
proceed()不会执行(相当于“阻断”) - 避免在
proceed()前修改关键对象(如把ParameterHandler替换为不可序列化对象),可能破坏后续流程 - 若需完全绕过原逻辑(如缓存命中直接返回),就不调用
proceed(),而是直接return yourResult;
不调用 proceed() 就是“终止拦截”
插件不是强制增强,而是可选干预。例如实现本地二级缓存插件:
- 先查本地缓存 → 命中则
return cachedResult;(跳过proceed()) - 未命中才
Object result = invocation.proceed();,再存入缓存后返回
这种“条件放行”才是插件灵活的关键。


















