当前位置: 首页 > news >正文

手写MyBatis第46弹:多插件责任链模式的实现原理与执行顺序奥秘--MyBatis插件架构深度解析

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我。

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。 


  1. MyBatis插件架构深度解析:多插件责任链模式的实现原理与执行顺序奥秘

  2. 手写MyBatis(七):InterceptorChain设计与多插件嵌套代理的实现策略

  3. 从单插件到多插件:MyBatis插件系统的架构演进与责任链模式实践

  4. MyBatis插件执行顺序揭秘:为什么后配置的插件先执行?


在前一篇文章中,我们探讨了MyBatis插件的基本原理和单个插件的实现方式。然而,在实际的企业级应用中,我们往往需要同时使用多个插件来实现不同的功能,比如同时使用分页插件、性能监控插件和数据加密插件。今天,我们将深入探讨MyBatis如何通过精巧的责任链模式设计,实现对多个插件的协同管理,并解析其执行顺序的内在逻辑。

一、多插件管理的挑战:为何需要InterceptorChain?

单个插件的实现相对简单,但当多个插件同时存在时,就会面临一系列复杂的问题:

  1. 执行顺序问题:多个插件应该按照什么顺序执行?哪个插件的逻辑先处理,哪个后处理?

  2. 代理嵌套问题:如何确保每个插件都能正确拦截目标方法,而不会相互干扰?

  3. 统一管理问题:如何集中管理所有插件,避免散落在代码的各个角落?

MyBatis的解决方案是引入一个专门的管理器——InterceptorChain(拦截器链)。这个类的设计体现了经典的责任链模式(Chain of Responsibility Pattern),它将所有插件组织成一条链,让每个插件都有机会处理请求。

二、InterceptorChain的核心设计与实现

InterceptorChain的核心职责非常明确:管理和执行所有插件。它的实现通常包含以下关键部分:

 public class InterceptorChain {// 存储所有拦截器的列表private final List<Interceptor> interceptors = new ArrayList<>();// 添加插件到链中public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}// 对目标对象应用所有插件public Object pluginAll(Object target) {// 遍历所有插件for (Interceptor interceptor : interceptors) {// 检查插件是否声明要拦截此类对象if (interceptor instanceof Interceptor) {// 使用插件包装目标对象target = interceptor.plugin(target);}}return target;}// 获取所有插件public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

这个看似简单的类,却是整个多插件系统的核心。它的pluginAll方法实现了插件的嵌套代理机制。

三、多插件的工作流程:层层代理的魔法

多插件的工作流程可以分为配置阶段和运行时阶段:

1. 配置阶段:插件注册与链构建

在MyBatis初始化时,所有通过配置文件或注解声明的插件都会被添加到InterceptorChain中:

 // 在配置类中InterceptorChain chain = new InterceptorChain();chain.addInterceptor(new PaginationInterceptor());  // 分页插件chain.addInterceptor(new PerformanceInterceptor()); // 性能监控插件chain.addInterceptor(new EncryptionInterceptor());  // 数据加密插件

2. 运行时阶段:四大组件的代理包装

当需要创建ExecutorStatementHandlerParameterHandlerResultSetHandler时,框架会调用InterceptorChain.pluginAll()方法:

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {// 1. 创建原始的目标对象Executor executor = new SimpleExecutor(transaction);// 2. 应用所有插件executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
四、插件执行顺序的奥秘:后配置先执行

这是一个非常关键且容易混淆的概念:InterceptorChain中,后添加的插件会先执行。这是因为插件是通过嵌套代理的方式实现的,后添加的插件位于代理链的外层。

让我们通过一个具体的例子来理解这个过程:

 // 假设我们按顺序添加三个插件chain.addInterceptor(plugin1); // 最先添加chain.addInterceptor(plugin2); // 其次添加  chain.addInterceptor(plugin3); // 最后添加​// 当调用pluginAll时,实际的包装过程是:Object target = new SimpleExecutor(); // 原始对象target = plugin3.plugin(target);     // 最外层代理target = plugin2.plugin(target);     // 中间层代理target = plugin1.plugin(target);     // 最内层代理

最终的代理结构如下图所示:

从图中可以清晰看出,虽然plugin1最先被添加到链中,但它实际上是最内层的代理,因此最后执行。而plugin3最后被添加,却成为最外层的代理,最先执行。

这种"后进先出"的执行顺序有着重要的实际意义:后添加的插件可以对先添加的插件的处理结果进行再处理。例如,如果先添加数据加密插件,后添加SQL日志插件,那么日志插件记录的是加密后的SQL,这可能不是我们想要的。通过调整添加顺序,我们可以让日志插件先记录原始SQL,然后再由加密插件进行处理。

五、Plugin.wrap方法:代理机制的实现核心

在每个插件的plugin方法中,通常会调用一个统一的工具方法Plugin.wrap()

 public class MyPlugin implements Interceptor {@Overridepublic Object plugin(Object target) {// 使用Plugin工具类创建代理return Plugin.wrap(target, this);}@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 插件的具体逻辑return invocation.proceed();}}

Plugin.wrap()方法的内部实现是理解整个插件机制的关键:

 public class Plugin implements InvocationHandler {public static Object wrap(Object target, Interceptor interceptor) {// 1. 获取插件声明的要拦截的接口和方法Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);// 2. 检查目标对象是否实现了插件要拦截的接口Class<?> type = target.getClass();Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {// 3. 创建动态代理对象return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}}

这个方法的核心作用是:只为那些实现了插件声明要拦截的接口的目标对象创建代理。这样就避免了不必要的代理开销,也确保了插件只会拦截它真正关心的方法。

六、总结:责任链模式的价值

MyBatis的多插件机制是一个经典的责任链模式应用,它的价值在于:

  1. 解耦:每个插件只关注自己的功能,不需要知道其他插件的存在。

  2. 灵活扩展:可以轻松地添加、移除或调整插件,而不需要修改框架核心代码。

  3. 执行顺序可控:通过调整插件的添加顺序,可以控制插件的执行顺序。

  4. 职责单一:每个插件都有明确的职责范围,符合单一职责原则。

通过这种精巧的设计,MyBatis提供了一个极其强大且灵活的扩展机制,使得开发者可以在不修改框架源码的情况下,深度定制和增强框架的功能。这种设计思路值得我们深入学习和借鉴,无论是在框架设计还是日常业务开发中,责任链模式都是一种非常有价值的架构模式。


💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕我是程序员扣棣,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!

http://www.dtcms.com/a/361532.html

相关文章:

  • 【机器学习学习笔记】numpy基础2
  • 基于站点、模式、遥感多源降水数据融合技术应用
  • 基于单片机自行车码表/骑行运动监测
  • CVE Push Service | 高危漏洞实时情报自动化推送工具
  • Python备份实战专栏第4/6篇:Vue.js + Flask 打造企业级备份监控面板
  • SQLSERVER关键字:N
  • 构建编程知识体系:从菜鸟教程入门到指针精通的系统学习指南
  • 华东制造企业推荐的SD-WAN服务商排名
  • MySQL 8 窗口函数详解
  • 【Linux】终止线程
  • 旧物回收小程序:科技赋能,开启旧物新生之旅
  • 02-Media-1-acodec.py 使用G.711编码和解码音频的示例程序
  • 《投资-41》- 自然=》生物=》人类社会=》商业=》金融=》股市=》投资,其层层叠加构建中内在的相似的规律和规则
  • AR巡检系统:多源数据同步,开启工业智能化新纪元
  • 单链表的基本原理与实现
  • PyCharm 2025版本中新建python工程文件自动创建.venv的意义和作用
  • 【PCIE 系统】111 PCIE 设备 TYPE 0、TYPE 1
  • Google Gemini 2.5 Flash Image(Nano-Banana)震撼登场!人人都能免费用的AI修图神器!
  • 【开题答辩全过程】以 校园帮帮团跑腿系统的设计与实现为例,包含答辩的问题和答案
  • Leetcode 3664. Two-Letter Card Game
  • LeetCode 面试经典 150_滑动窗口_串联所有单词的子串(32_30_C++_困难)(滑动窗口:控制起点和滑动距离)
  • 原位表征技术在水系电池研究稳定性测试中的应用-测试GO
  • 教育 AI 的下半场:个性化学习路径生成背后,技术如何平衡效率与教育本质?
  • 学习日记-spring-day47-9.1
  • 使用LoadBalancer替换Ribbon(五)
  • 深入解析quiche开源项目:从QUIC协议到云原生实践
  • 每日算法题【二叉树】:计算二叉树节点的个数、叶子结点的个数、第k层节点的个数
  • 【面试场景题】不使用redis、zk如何自己开发一个分布式锁
  • 数据库索引失效的原因+示例
  • 视觉引导机械手双夹爪抓取:偏心旋转补偿与逆运动学求解