MyBatis插件执行顺序由注册顺序决定:先注册的插件位于代理链最外层,最先执行;同目标对象按注册顺序嵌套调用,不同目标对象则遵循Executor→StatementHandler→ParameterHandler→ResultSetHandler的固有流程。

MyBatis 多个插件串联时,执行顺序和加载逻辑由“注册顺序”决定,不是配置顺序或类名顺序,也不是按功能重要性排——谁先注册,谁就包在最外层,也就最先被触发。
插件注册顺序决定执行先后
插件链本质是代理嵌套:每个插件对目标对象(如 Executor)做一次包装,形成 插件A → 插件B → 原对象 的结构。调用时,从最外层开始进入,逐层向内,最后执行原始方法;返回时再逐层向外。
- XML 配置中,
<plugin>标签的书写顺序即注册顺序:
先写MyInterceptor1,后写MyInterceptor2,则 1 包裹 2,1 先执行 - 代码注册时,
configuration.addInterceptor()的调用顺序就是注册顺序 - Spring Boot 环境下,用
@Order注解控制 Bean 注入顺序,进而影响注册顺序:@Order(1)的插件比@Order(2)的更早注册,也更先执行
同一目标对象上的拦截器按链式包裹执行
所有插件若都声明拦截同一个对象(比如都是 @Intercepts(@Signature(type = Executor.class, ...))),它们会统一被 InterceptorChain.pluginAll() 处理:遍历 interceptors 列表,依次调用每个插件的 plugin() 方法,层层 wrap 目标对象。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 列表索引 0 的插件生成最外层代理,它的
intercept()最先被调用 - 每个插件内部必须调用
invocation.proceed(),才能把调用继续传给下一层 - 不调用
proceed()就会中断链,后续插件和原方法都不会执行
不同目标对象之间的拦截有固定流程顺序
MyBatis 内部执行 SQL 时,四大对象的创建和调用是有严格流程的:
Executor → StatementHandler → ParameterHandler → ResultSetHandler(中间 StatementHandler 可能出现多次,但整体优先级不变)。
立即学习“Java免费学习笔记(深入)”;
- 一个插件只拦截
Executor,另一个只拦截ResultSetHandler,它们不会互相嵌套,而是分别在各自对象的生命周期中独立触发 - 执行阶段,先走到 Executor 的代理链,再走到 StatementHandler 的代理链……依此类推
- 所以“哪个插件先执行”,要分两层看:同对象看注册顺序,不同对象看 MyBatis 执行流程
PageHelper 等特殊插件的顺序处理
像 PageHelper 这类需要抢占先机的插件,会主动干预注册时机。它利用 InitializingBean.afterPropertiesSet(),在所有常规插件注册完成后,把自己追加到 interceptors 列表末尾——这样它就成为最外层代理,在所有其他插件之前执行。
- 这是绕过默认顺序的“手动置顶”策略,属于框架级技巧
- 普通业务插件不建议模仿,应通过规范注册方式(XML/代码/@Order)明确表达意图
- 若多个插件都试图“抢第一”,容易引发不可控行为,需人工协调注册逻辑

















