如何自定義一個MyBatis插件
《如何自定義一個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)
暫無評論,來搶第一條。