阅界资讯

如何自定义一个MyBatis插件

阅阅界编辑部2阅读20分钟

《如何自定义一个MyBatis插件》是近期博客园技术区的好帖,信息密度大。下面分段讲清它的核心内容,看到结尾你就知道该怎么做了。

如何自定义一个MyBatis插件 面试官您好,我来回答一下如何自定义 MyBatis 插件这个问题。 先讲核心原理 🧠 MyBatis 插件本质上是基于 JDK 动态代理 + 责任链模式 实现的,它允许我们在 MyBatis 执行 SQL 的关键节点进行拦截和增强。 MyBatis 定义了 四大核心对象 可以被拦截: 拦截对象 主要作用 常用拦截方法 Executor 执行增删改查的调度器 update、query、commit、rollback StatementHandler 处理 SQL 语句构建 prepare、parameterize、batch、update、query ParameterHandler 处理 SQL 参数映射 setParameters ResultSetHandler 处理结果集映射 handleResultSets、handleOutputParameters 自定义插件的完整步骤 📝 我用一个流程图来展示完整的开发流程: 关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。

如何自定义一个MyBatis插件 面试官您好,我来回答一下如何自定义 MyBatis 插件这个问题。 先讲核心原理 🧠 MyBatis 插件本质上是基于 JDK 动态代理 + 责任链模式 实现的,它允许我们在 MyBatis 执行 SQL 的关键节点进行拦截和增强。 MyBatis 定义了 四大核心对象 可以被拦截: 拦截对象 主要作用 常用拦截方法 Executor 执行增删改查的调度器 update、query、commit、rollback StatementHandler 处理 SQL 语句构建 prepare、parameterize、batch、update、query ParameterHandler 处理 SQL 参数映射 setParameters ResultSetHandler 处理结果集映射 handleResultSets、handleOutputParameters 自定义插件的完整步骤 📝 我用一个流程图来展示完整的开发流程: 关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。 关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。

如何自定义一个MyBatis插件 面试官您好,我来回答一下如何自定义 MyBatis 插件这个问题。 先讲核心原理 🧠 MyBatis 插件本质上是基于 JDK 动态代理 + 责任链模式 实现的,它允许我们在 MyBatis 执行 SQL 的关键节点进行拦截和增强。 MyBatis 定义了 四大核心对象 可以被拦截: 拦截对象 主要作用 常用拦截方法 Executor 执行增删改查的调度器 update、query、commit、rollback StatementHandler 处理 SQL 语句构建 prepare、parameterize、batch、update、query ParameterHandler 处理 SQL 参数映射 setParameters ResultSetHandler 处理结果集映射 handleResultSets、handleOutputParameters 自定义插件的完整步骤 📝 我用一个流程图来展示完整的开发流程: 关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。 关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。 1. 基础骨架代码 [代码示例略] 2. 注册插件 方式一:XML 配置 [代码示例略] 方式二:SpringBoot 自动配置 [代码示例略] 关键技术点 ⚡ @Intercepts 和 @Signature 注解 :精确指定要拦截哪个对象的哪个方法 Invocation 对象 :包含了被代理对象、方法和参数,调用 proceed() 执行原方法 MetaObject 工具类 :MyBatis 提供的反射工具,方便获取对象的私有属性 Plugin.wrap() :自动判断是否需要生成代理对象,只有匹配的对象才会被代理 常见应用场景 🎯 SQL 执行日志打印(带参数) 分页插件(如 PageHelper) 数据权限过滤 慢 SQL 监控 读写分离 公共字段自动填充 注意事项和坑点 ⚠️ 拦截顺序问题 :多个插件会形成责任链,先注册的先拦截,后注册的先执行 不要随意修改原对象 :除非你非常清楚后果,否则可能导致不可预期的问题 性能影响 :拦截器会增加额外的方法调用开销,不要在拦截器中做耗时操作 异常处理 :拦截器中的异常会影响 SQL 执行,需要妥善处理 版本兼容性 :不同版本的 MyBatis 方法签名可能会有变化 核心代码与技术亮点 🚀 我给您展示一个 实际项目中使用的数据权限过滤插件 ,这是我觉得最能体现技术深度的示例: [代码示例略] 核心技术亮点总结 ✨ 精确执行顺序控制 :通过 @Order 注解明确指定插件执行顺序,完美解决与分页插件的冲突 安全的 SQL 修改方式 :基于抽象语法树(AST)修改 SQL,比字符串替换更可靠、更健壮 优雅的反射操作 :使用 MyBatis 官方 MetaObject 工具,一行代码获取 / 修改任何私有属性 灵活的白名单机制 :支持按方法名排除不需要权限控制的接口 完全解耦的设计 :从统一安全上下文获取用户信息,插件与业务代码零耦合 常见技术难点与解决方案 🧩 我整理了实际开发中最容易遇到的 7 个技术难点,以及对应的解决方案: 技术难点 核心问题 解决方案 关键代码示例 SQL 修改导致语法错误 简单字符串替换无法处理复杂 SQL(子查询、OR 条件、多表联查) 使用 JSqlParser 解析 SQL 抽象语法树,动态修改表达式节点 Select select=(Select) parserManager.parse(new StringReader(sql)); 多插件执行顺序混乱 分页插件在数据权限插件之前执行,导致分页后再过滤数据 实现 Ordered 接口或使用 @Order 注解规则:数值越小,优先级越高 @Order(1) // 数据权限 @Order(2) // 分页 复杂参数无法获取 无法获取嵌套对象、集合、 @Param 注解的参数值 使用 MetaObject 递归获取参数值,支持 OGNL 表达式 Object deptId=metaObject.getValue("user.dept.id"); 批量操作无法拦截 批量插入 / 更新时,拦截器只执行一次 拦截 Executor 的batch方法,遍历所有待执行语句 @Signature(type=Executor.class, method="batch", args={MappedStatement.class, Object.class}) 动态 SQL 修改无效 拦截时机不对,动态 SQL 还未解析完成 拦截 StatementHandler 的 prepare 方法,此时 SQL 已完全解析 @Signature(type=StatementHandler.class, method="prepare", args={Connection.class, Integer.class}) 版本兼容性问题 不同 MyBatis 版本的内部对象结构不同 使用反射获取方法和属性,避免直接调用版本相关 API Method getBoundSql=target.getClass().getMethod("getBoundSql"); 无法区分不同 Mapper 方法 所有方法都被拦截,无法精确控制 通过 MappedStatement 的id获取方法全名,进行白名单 / 黑名单过滤 String methodId=ms.getId(); 回答总结 ✅ 自定义 MyBatis 插件的核心就是: 实现 Interceptor 接口,通过注解指定拦截点,在 intercept 方法中编写增强逻辑,最后注册插件 。

如何自定义一个MyBatis插件 面试官您好,我来回答一下如何自定义 MyBatis 插件这个问题。 先讲核心原理 🧠 MyBatis 插件本质上是基于 JDK 动态代理 + 责任链模式 实现的,它允许我们在 MyBatis 执行 SQL 的关键节点进行拦截和增强。 MyBatis 定义了 四大核心对象 可以被拦截: 拦截对象 主要作用 常用拦截方法 Executor 执行增删改查的调度器 update、query、commit、rollback StatementHandler 处理 SQL 语句构建 prepare、parameterize、batch、update、query ParameterHandler 处理 SQL 参数映射 setParameters ResultSetHandler 处理结果集映射 handleResultSets、handleOutputParameters 自定义插件的完整步骤 📝 我用一个流程图来展示完整的开发流程: 关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。 关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。 1. 基础骨架代码 [代码示例略] 2. 注册插件 方式一:XML 配置 [代码示例略] 方式二:SpringBoot 自动配置 [代码示例略] 关键技术点 ⚡ @Intercepts 和 @Signature 注解 :精确指定要拦截哪个对象的哪个方法 Invocation 对象 :包含了被代理对象、方法和参数,调用 proceed() 执行原方法 MetaObject 工具类 :MyBatis 提供的反射工具,方便获取对象的私有属性 Plugin.wrap() :自动判断是否需要生成代理对象,只有匹配的对象才会被代理 常见应用场景 🎯 SQL 执行日志打印(带参数) 分页插件(如 PageHelper) 数据权限过滤 慢 SQL 监控 读写分离 公共字段自动填充 注意事项和坑点 ⚠️ 拦截顺序问题 :多个插件会形成责任链,先注册的先拦截,后注册的先执行 不要随意修改原对象 :除非你非常清楚后果,否则可能导致不可预期的问题 性能影响 :拦截器会增加额外的方法调用开销,不要在拦截器中做耗时操作 异常处理 :拦截器中的异常会影响 SQL 执行,需要妥善处理 版本兼容性 :不同版本的 MyBatis 方法签名可能会有变化 核心代码与技术亮点 🚀 我给您展示一个 实际项目中使用的数据权限过滤插件 ,这是我觉得最能体现技术深度的示例: [代码示例略] 核心技术亮点总结 ✨ 精确执行顺序控制 :通过 @Order 注解明确指定插件执行顺序,完美解决与分页插件的冲突 安全的 SQL 修改方式 :基于抽象语法树(AST)修改 SQL,比字符串替换更可靠、更健壮 优雅的反射操作 :使用 MyBatis 官方 MetaObject 工具,一行代码获取 / 修改任何私有属性 灵活的白名单机制 :支持按方法名排除不需要权限控制的接口 完全解耦的设计 :从统一安全上下文获取用户信息,插件与业务代码零耦合 常见技术难点与解决方案 🧩 我整理了实际开发中最容易遇到的 7 个技术难点,以及对应的解决方案: 技术难点 核心问题 解决方案 关键代码示例 SQL 修改导致语法错误 简单字符串替换无法处理复杂 SQL(子查询、OR 条件、多表联查) 使用 JSqlParser 解析 SQL 抽象语法树,动态修改表达式节点 Select select=(Select) parserManager.parse(new StringReader(sql)); 多插件执行顺序混乱 分页插件在数据权限插件之前执行,导致分页后再过滤数据 实现 Ordered 接口或使用 @Order 注解规则:数值越小,优先级越高 @Order(1) // 数据权限 @Order(2) // 分页 复杂参数无法获取 无法获取嵌套对象、集合、 @Param 注解的参数值 使用 MetaObject 递归获取参数值,支持 OGNL 表达式 Object deptId=metaObject.getValue("user.dept.id"); 批量操作无法拦截 批量插入 / 更新时,拦截器只执行一次 拦截 Executor 的batch方法,遍历所有待执行语句 @Signature(type=Executor.class, method="batch", args={MappedStatement.class, Object.class}) 动态 SQL 修改无效 拦截时机不对,动态 SQL 还未解析完成 拦截 StatementHandler 的 prepare 方法,此时 SQL 已完全解析 @Signature(type=StatementHandler.class, method="prepare", args={Connection.class, Integer.class}) 版本兼容性问题 不同 MyBatis 版本的内部对象结构不同 使用反射获取方法和属性,避免直接调用版本相关 API Method getBoundSql=target.getClass().getMethod("getBoundSql"); 无法区分不同 Mapper 方法 所有方法都被拦截,无法精确控制 通过 MappedStatement 的id获取方法全名,进行白名单 / 黑名单过滤 String methodId=ms.getId(); 回答总结 ✅ 自定义 MyBatis 插件的核心就是: 实现 Interceptor 接口,通过注解指定拦截点,在 intercept 方法中编写增强逻辑,最后注册插件 。 它是 MyBatis 提供的最强大的扩展机制之一,让我们可以在不修改框架源码的情况下,实现很多通用的横切功能。

如何自定义一个MyBatis插件 面试官您好,我来回答一下如何自定义 MyBatis 插件这个问题。 先讲核心原理 🧠 MyBatis 插件本质上是基于 JDK 动态代理 + 责任链模式 实现的,它允许我们在 MyBatis 执行 SQL 的关键节点进行拦截和增强。 MyBatis 定义了 四大核心对象 可以被拦截: 拦截对象 主要作用 常用拦截方法 Executor 执行增删改查的调度器 update、query、commit、rollback StatementHandler 处理 SQL 语句构建 prepare、parameterize、batch、update、query ParameterHandler 处理 SQL 参数映射 setParameters ResultSetHandler 处理结果集映射 handleResultSets、handleOutputParameters 自定义插件的完整步骤 📝 我用一个流程图来展示完整的开发流程: 关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。 关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。 1. 基础骨架代码 [代码示例略] 2. 注册插件 方式一:XML 配置 [代码示例略] 方式二:SpringBoot 自动配置 [代码示例略] 关键技术点 ⚡ @Intercepts 和 @Signature 注解 :精确指定要拦截哪个对象的哪个方法 Invocation 对象 :包含了被代理对象、方法和参数,调用 proceed() 执行原方法 MetaObject 工具类 :MyBatis 提供的反射工具,方便获取对象的私有属性 Plugin.wrap() :自动判断是否需要生成代理对象,只有匹配的对象才会被代理 常见应用场景 🎯 SQL 执行日志打印(带参数) 分页插件(如 PageHelper) 数据权限过滤 慢 SQL 监控 读写分离 公共字段自动填充 注意事项和坑点 ⚠️ 拦截顺序问题 :多个插件会形成责任链,先注册的先拦截,后注册的先执行 不要随意修改原对象 :除非你非常清楚后果,否则可能导致不可预期的问题 性能影响 :拦截器会增加额外的方法调用开销,不要在拦截器中做耗时操作 异常处理 :拦截器中的异常会影响 SQL 执行,需要妥善处理 版本兼容性 :不同版本的 MyBatis 方法签名可能会有变化 核心代码与技术亮点 🚀 我给您展示一个 实际项目中使用的数据权限过滤插件 ,这是我觉得最能体现技术深度的示例: [代码示例略] 核心技术亮点总结 ✨ 精确执行顺序控制 :通过 @Order 注解明确指定插件执行顺序,完美解决与分页插件的冲突 安全的 SQL 修改方式 :基于抽象语法树(AST)修改 SQL,比字符串替换更可靠、更健壮 优雅的反射操作 :使用 MyBatis 官方 MetaObject 工具,一行代码获取 / 修改任何私有属性 灵活的白名单机制 :支持按方法名排除不需要权限控制的接口 完全解耦的设计 :从统一安全上下文获取用户信息,插件与业务代码零耦合 常见技术难点与解决方案 🧩 我整理了实际开发中最容易遇到的 7 个技术难点,以及对应的解决方案: 技术难点 核心问题 解决方案 关键代码示例 SQL 修改导致语法错误 简单字符串替换无法处理复杂 SQL(子查询、OR 条件、多表联查) 使用 JSqlParser 解析 SQL 抽象语法树,动态修改表达式节点 Select select=(Select) parserManager.parse(new StringReader(sql)); 多插件执行顺序混乱 分页插件在数据权限插件之前执行,导致分页后再过滤数据 实现 Ordered 接口或使用 @Order 注解规则:数值越小,优先级越高 @Order(1) // 数据权限 @Order(2) // 分页 复杂参数无法获取 无法获取嵌套对象、集合、 @Param 注解的参数值 使用 MetaObject 递归获取参数值,支持 OGNL 表达式 Object deptId=metaObject.getValue("user.dept.id"); 批量操作无法拦截 批量插入 / 更新时,拦截器只执行一次 拦截 Executor 的batch方法,遍历所有待执行语句 @Signature(type=Executor.class, method="batch", args={MappedStatement.class, Object.class}) 动态 SQL 修改无效 拦截时机不对,动态 SQL 还未解析完成 拦截 StatementHandler 的 prepare 方法,此时 SQL 已完全解析 @Signature(type=StatementHandler.class, method="prepare", args={Connection.class, Integer.class}) 版本兼容性问题 不同 MyBatis 版本的内部对象结构不同 使用反射获取方法和属性,避免直接调用版本相关 API Method getBoundSql=target.getClass().getMethod("getBoundSql"); 无法区分不同 Mapper 方法 所有方法都被拦截,无法精确控制 通过 MappedStatement 的id获取方法全名,进行白名单 / 黑名单过滤 String methodId=ms.getId(); 回答总结 ✅ 自定义 MyBatis 插件的核心就是: 实现 Interceptor 接口,通过注解指定拦截点,在 intercept 方法中编写增强逻辑,最后注册插件 。 它是 MyBatis 提供的最强大的扩展机制之一,让我们可以在不修改框架源码的情况下,实现很多通用的横切功能。实际开发中,建议优先使用基于抽象语法树的 SQL 修改方式,并通过 @Order 注解明确控制多插件的执行顺序,这样能避免 90% 以上的坑。

行动建议:收藏这篇,下次碰到同类问题先翻出来对照做一遍。好经验的价值,在于用起来。你最近被这类问题卡过吗? 来源|博客园《如何自定义一个MyBatis插件》,https://www.cnblogs.com/zrui-xyu/p/22976656.html

前沿科技职业观察

评论(0)

暂无评论,来抢第一条。