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

手写MyBatis第53弹: @Intercepts与@Signature注解的工作原理

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

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

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

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

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

目录

正文

一、MyBatis插件机制概述

二、@Intercepts和@Signature注解详解

2.1 注解定义与作用

2.2 为什么需要args属性?

三、注解的工作原理

3.1 注册与发现机制

3.2 拦截点匹配算法

四、实战:自定义插件开发

4.1 基本拦截器实现

4.2 复杂场景:参数修改插件

五、高级应用与最佳实践

5.1 多方法拦截策略

5.2 性能优化考虑

六、常见问题与解决方案

6.1 拦截器不生效的可能原因

6.2 调试技巧

七、总结


  1. MyBatis拦截器核心原理解析:@Intercepts和@Signature注解的深度剖析

  2. 精准拦截的艺术:MyBatis插件开发中@Signature注解的完整指南

  3. 从源码角度理解MyBatis拦截器:为什么需要args属性来精确匹配方法?

  4. MyBatis插件开发实战:使用注解实现方法级精确拦截的技巧与最佳实践

  5. 超越基础:深入MyBatis拦截器机制与自定义注解的高级用法

正文

在MyBatis的插件机制中,@Intercepts@Signature两个注解扮演着至关重要的角色。它们就像是插件系统的"导航系统",精确指导框架应该拦截哪个类的哪个方法。本文将深入剖析这两个注解的工作原理、实现机制以及实际应用场景。

一、MyBatis插件机制概述

MyBatis的插件机制是基于Java动态代理实现的拦截器模式,允许用户在已映射语句执行过程中的某些点进行拦截和自定义处理。这种设计遵循了开闭原则,使得我们可以增强MyBatis的功能而不需要修改其核心代码。

在MyBatis的架构中,四个核心组件可以被拦截:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

  • ParameterHandler (getParameterObject, setParameters)

  • ResultSetHandler (handleResultSets, handleOutputParameters)

  • StatementHandler (prepare, parameterize, batch, update, query)

二、@Intercepts和@Signature注解详解

2.1 注解定义与作用

@Intercepts注解用于声明一个类作为MyBatis拦截器,并包含一个或多个@Signature注解:

 @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Intercepts {Signature[] value();}

@Signature注解则用于精确指定要拦截的方法:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({})public @interface Signature {Class<?> type();String method();Class<?>[] args();}
2.2 为什么需要args属性?

这是本文要解答的核心问题。args属性的存在是为了精确匹配方法重载。在Java中,方法签名由方法名和参数类型列表共同决定,仅仅使用方法名无法唯一确定一个方法。

考虑以下场景:

class Example {public void process(String arg) {}public void process(String arg1, String arg2) {}public void process(Integer arg) {}}

如果我们只想拦截process(String arg)方法,而不拦截其他重载版本,就必须使用args属性来指定参数类型列表。没有args属性,拦截器将无法区分这些重载方法,可能导致错误的拦截或不必要的性能开销。

三、注解的工作原理

3.1 注册与发现机制

MyBatis在启动过程中会扫描所有配置的拦截器,并通过反射读取其上的@Intercepts@Signature注解信息。这个过程主要包括以下步骤:

  1. 配置解析:解析mybatis-config.xml中的plugin配置

  2. 实例化拦截器:创建拦截器实例

  3. 注解解析:通过反射获取拦截器类上的注解信息

  4. 方法签名映射:建立方法签名与拦截器的映射关系

  5. 代理对象创建:为目标对象创建代理,插入拦截逻辑

3.2 拦截点匹配算法

当MyBatis执行一个方法时,拦截器系统会执行以下匹配算法:

  1. 获取目标对象的类和方法名

  2. 获取方法参数类型列表

  3. 遍历所有已注册的拦截器签名

  4. 比对类型、方法名和参数类型是否完全匹配

  5. 如果匹配成功,则按顺序执行拦截器链

四、实战:自定义插件开发

4.1 基本拦截器实现

下面是一个简单的SQL执行时间统计插件示例:

 @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class SqlExecuteTimeInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed();long end = System.currentTimeMillis();System.out.println("SQL执行耗时: " + (end - start) + "ms");return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可接收配置参数}}
4.2 复杂场景:参数修改插件

在某些场景下,我们可能需要修改SQL参数,下面是一个参数加密插件:

 @Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})})public class ParameterEncryptInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();// 通过反射获取parameterObjectField parameterObjectField = parameterHandler.getClass().getDeclaredField("parameterObject");parameterObjectField.setAccessible(true);Object parameterObject = parameterObjectField.get(parameterHandler);// 对参数进行加密处理if (parameterObject != null) {Object encryptedParams = encryptParameters(parameterObject);parameterObjectField.set(parameterHandler, encryptedParams);}return invocation.proceed();}private Object encryptParameters(Object parameterObject) {// 实现参数加密逻辑return parameterObject;}// plugin和setProperties方法省略}

五、高级应用与最佳实践

5.1 多方法拦截策略

当一个插件需要拦截多个不相关的方法时,可以采用策略模式来组织拦截逻辑:

 @Intercepts({@Signature(type = Executor.class, method = "update", args = {...}),@Signature(type = StatementHandler.class, method = "prepare", args = {...}),@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {...})})public class MultiPurposeInterceptor implements Interceptor {private Map<String, InterceptionStrategy> strategies;public MultiPurposeInterceptor() {strategies = new HashMap<>();strategies.put("Executor.update", new ExecutorUpdateStrategy());strategies.put("StatementHandler.prepare", new StatementPrepareStrategy());strategies.put("ResultSetHandler.handleResultSets", new ResultSetHandleStrategy());}@Overridepublic Object intercept(Invocation invocation) throws Throwable {String key = invocation.getMethod().getDeclaringClass().getSimpleName() + "." + invocation.getMethod().getName();InterceptionStrategy strategy = strategies.get(key);if (strategy != null) {return strategy.execute(invocation);}return invocation.proceed();}// 策略接口和实现类省略}
5.2 性能优化考虑

拦截器会增加方法调用的开销,特别是在拦截高频方法时。以下是一些优化建议:

  1. 精确拦截:只拦截必要的方法,使用准确的args定义

  2. 快速失败:在intercept方法中尽早判断是否需要处理

  3. 避免重复计算:缓存昂贵的操作结果

  4. 异步处理:将非核心逻辑异步化

六、常见问题与解决方案

6.1 拦截器不生效的可能原因
  1. 注解配置错误:type、method或args不匹配实际方法

  2. 配置顺序问题:在mybatis-config.xml中配置顺序错误

  3. 代理对象问题:目标对象已经被多次代理

  4. 版本兼容性问题:不同MyBatis版本方法签名可能有变化

6.2 调试技巧
  1. 启用MyBatis日志,查看代理创建过程

  2. 使用反射打印目标方法的具体签名

  3. 在plugin方法中添加日志,确认拦截器被正确包装

七、总结

@Intercepts@Signature注解是MyBatis插件机制的基石,它们通过精确的方法签名匹配实现了灵活的拦截功能。理解这两个注解的工作原理,特别是args属性的重要性,对于开发高效、稳定的MyBatis插件至关重要。

在实际开发中,我们应该:

  1. 精确指定要拦截的方法签名,避免过度拦截

  2. 理解MyBatis各组件的生命周期和职责

  3. 遵循最佳实践,确保插件的性能和稳定性

  4. 充分利用注解提供的元数据信息进行高效拦截

通过深入理解和合理应用这两个注解,我们可以开发出功能强大、性能优异的MyBatis插件,极大地扩展MyBatis的能力边界。


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

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

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


文章转载自:

http://D0zYjqSK.mftdq.cn
http://rbBqFQqX.mftdq.cn
http://mX4XZB07.mftdq.cn
http://X7GfTqkw.mftdq.cn
http://P6RV47Lp.mftdq.cn
http://jXwr6z96.mftdq.cn
http://erbp9KcZ.mftdq.cn
http://qY5jXzaZ.mftdq.cn
http://DOjdhGWY.mftdq.cn
http://fDP1aaQp.mftdq.cn
http://BoLj5Hlk.mftdq.cn
http://LRRq9hyn.mftdq.cn
http://rYdQjjVY.mftdq.cn
http://hgcUX9he.mftdq.cn
http://tZTfbOmm.mftdq.cn
http://I181Pd1r.mftdq.cn
http://dgLW5Q3T.mftdq.cn
http://q22CHUEQ.mftdq.cn
http://Y20Ltfx2.mftdq.cn
http://NeVGabOI.mftdq.cn
http://MuG2XkMK.mftdq.cn
http://pAOTQhM7.mftdq.cn
http://KqMUQDPn.mftdq.cn
http://uoomMLBo.mftdq.cn
http://vgRqcz4l.mftdq.cn
http://47DCymV9.mftdq.cn
http://pZn1eGU4.mftdq.cn
http://jWv3UcxJ.mftdq.cn
http://yIaHIsYy.mftdq.cn
http://a9hdy3jN.mftdq.cn
http://www.dtcms.com/a/370098.html

相关文章:

  • 基于SpringBoot+JSP开发的潮鞋网络商城
  • docker run 命令,不接it选项,run一个centos没有显示在运行,而run一个nginx却可以呢?
  • 【C++框架#3】Etcd 安装使用
  • 洛谷 P3178 [HAOI2015] 树上操作-提高+/省选-
  • Java全栈开发工程师的面试实战:从基础到复杂场景的技术探索
  • 【Flask】测试平台开发,重构提测管理页面-第二十篇
  • ICPC 2023 Nanjing R L 题 Elevator
  • TensorFlow 面试题及详细答案 120道(101-110)-- 底层原理与扩展
  • 《sklearn机器学习——聚类性能指标》Davies-Bouldin Index (戴维斯-博尔丁指数)
  • 美团9-6:编程题
  • 深度学习--自然语言预处理--- Word2Vec
  • Nikto 漏洞扫描工具使用指南
  • Redis(46) 如何搭建Redis哨兵?
  • Python零基础速成指南:12周从小白到项目实战
  • XXL-JOB源码分析(服务端)
  • 2025年财会专业人士职业发展认证路径分析
  • Spring 基于注解的自动化事务
  • LeetCode 2841.几乎唯一子数组的最大和
  • qt ElaWidgetTools添加Page页面
  • simd学习
  • 【Linux指南】动静态库与链接机制:从原理到实践
  • 分布式通信平台测试报告
  • LeetCode算法日记 - Day 33: 最长公共前缀、最长回文子串
  • 能发弹幕的简单视频网站
  • 【开题答辩全过程】以 基于Hadoop电商数据的可视化分析为例,包含答辩的问题和答案
  • 苍穹外卖优化-续
  • vi中的常用快捷键
  • 如何使显示器在笔记本盖上盖子时还能正常运转
  • 09_多态
  • 用 Go + HTML 实现 OpenHarmony 投屏(hdckit-go + WebSocket + Canvas 实战)