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

手写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://www.xdnf.cn/news/20200.html

相关文章:

  • 工业洗地机和商用洗地机的区别是什么?
  • 【基础-单选】关于bundleName,下列说法正确的是?
  • 波特率vs比特率
  • rh134第三章复习总结
  • 贪心算法应用:保险理赔调度问题详解
  • Java中的死锁
  • 使用 MongoDB.Driver 在 C# .NETCore 中实现 Mongo DB 过滤器
  • [数据结构] ArrayList(顺序表)与LinkedList(链表)
  • 万代《宝可梦》主题新品扭蛋公开!史上最大尺寸
  • 机器人控制器开发(传感器层——奥比大白相机适配)
  • 【FastDDS】Layer Transport ( 05-Shared Memory Transport)
  • 天气预报云服务器部署实战
  • 在Java AI项目中实现Function Call功能
  • 计算机毕设大数据方向:基于Spark+Hadoop的餐饮外卖平台数据分析系统【源码+文档+调试】
  • 通过Idea 阿里插件快速部署java jar包
  • 实用向:Linux Shell 脚本实现 CPU / 内存 / 硬盘 + IO / 网络多指标告警(支持 163/QQ/139 邮箱)
  • python调用mysql
  • PDF文件基础-计算机字体
  • 【Luogu_P8118】 「RdOI R3.5」Mystery【Slope Trick】【DP】
  • 深度学习基础概念回顾(Pytorch架构)
  • 【Java实战㉗】Java日志框架实战:Logback与Log4j2的深度探索
  • 大型Go项目中搭建CI/CD流水线
  • 竞价代运营:百度竞价账户托管优化
  • VeeValidate v4 终极指南:精通 Vue 3 组合式 API 表单验证
  • Web Worker 从原理到实战 —— 把耗时工作搬到后台线程,避免页面卡顿
  • 计算机视觉(九):图像轮廓
  • 破局功能割裂、成本高昂、协同低效,遨游天通卫星电话实现一机多能
  • Adobe Illustrator(Ai) 2022矢量设计软件的安装教程与下载地址
  • 【Python自动化】 21.3 Pandas Series 核心数据结构完全指南
  • 如何使显示器在笔记本盖上盖子时还能正常运转