🥂(?′?`?)您的點贊👍?評論📝?收藏?是作者創作的最大動力🤞
💖📕🎉🔥 支持我:點贊👍+收藏??+留言📝歡迎留言討論
🔥🔥🔥(源碼 + 調試運行 + 問題答疑)
🔥🔥🔥 ?有興趣可以聯系我。
我們常常在當下感到時間慢,覺得未來遙遠,但一旦回頭看,時間已經悄然流逝。對于未來,盡管如此,也應該保持一種從容的態度,相信未來仍有許多可能性等待著我們。?
目錄
一、Interceptor接口:插件系統的契約
二、Invocation對象:調用上下文的完整封裝
三、proceed()方法:鏈式調用的引擎
四、Plugin.wrap方法:代理生成的工廠
五、插件如何修改參數和返回值
六、總結:鏈式調用的設計哲學
-
MyBatis插件鏈式調用深度解析:Interceptor接口設計與Invocation上下文傳遞機制
-
手寫MyBatis(八):插件鏈式調用與Invocation.proceed的遞歸魔法
-
從攔截到修改:MyBatis插件如何操縱方法參數與返回值的核心技術
-
Plugin.wrap方法揭秘:MyBatis動態代理生成與方法攔截的精妙實現
在上一篇文章中,我們探討了MyBatis多插件管理的責任鏈模式。今天,我們將深入這個鏈條的內部,解析鏈式調用的實現細節,特別是Interceptor
接口的設計、Invocation
對象的作用以及proceed()
方法如何實現遞歸調用。這些看似簡單的組件背后,隱藏著MyBatis插件系統最精妙的設計思想。
一、Interceptor接口:插件系統的契約
Interceptor
接口是MyBatis插件系統的核心契約,它定義了插件必須實現的三個關鍵行為:
?public interface Interceptor {// 核心攔截方法:包含插件的主要邏輯Object intercept(Invocation invocation) throws Throwable;// 默認方法:用于包裝目標對象生成代理default Object plugin(Object target) {return Plugin.wrap(target, this);}// 設置插件屬性(可選)default void setProperties(Properties properties) {// 默認空實現}}
這個接口設計的精妙之處在于:
-
intercept
方法:這是插件的核心,包含了插件的主要業務邏輯。它接收一個Invocation
參數,這個參數封裝了完整的調用上下文。 -
plugin
默認方法:這是一個非常巧妙的設計。通過提供默認實現,MyBatis讓插件開發者無需關心復雜的代理生成邏輯,只需要專注于業務邏輯的實現。這個方法確保了所有插件都使用統一的代理生成機制。 -
setProperties
方法:允許插件接收外部配置參數,增強了插件的靈活性。
二、Invocation對象:調用上下文的完整封裝
Invocation
對象是插件鏈式調用的核心載體,它封裝了一次方法調用的所有必要信息:
?public class Invocation {private final Object target; ? ? // 被代理的原始對象private final Method method; ? ? // 被攔截的方法private final Object[] args; ? ? // 方法參數public Invocation(Object target, Method method, Object[] args) {this.target = target;this.method = method;this.args = args;}// 關鍵方法:繼續執行調用鏈public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);}// Getter方法public Object getTarget() { return target; }public Method getMethod() { return method; }public Object[] getArgs() { return args; }// 設置參數(用于修改參數)public void setArgs(Object[] args) { this.args = args; }}
Invocation
的設計體現了"信息專家"模式——它將一次方法調用的所有相關信息集中管理,為插件提供了完整的操作上下文。
三、proceed()方法:鏈式調用的引擎
proceed()
方法是整個插件鏈式調用機制的核心。它的作用看似簡單——調用目標方法,但在責任鏈模式中,它的行為實際上要復雜得多:
從圖中可以看出,proceed()
方法實際上觸發了一個遞歸的調用過程:
-
最外層插件首先執行前置處理邏輯
-
調用
proceed()
,該方法實際上會調用下一個插件的intercept
方法 -
這個過程遞歸進行,直到最后一個插件調用
proceed()
-
最后一個
proceed()
調用原始目標方法 -
然后調用棧逐層返回,每個插件執行后置處理邏輯
-
最終返回到最外層插件,完成整個調用鏈
這種設計的美妙之處在于:每個插件都無需知道整個調用鏈的結構,它只需要調用proceed()
并將處理權交給鏈條的下一個環節即可。
四、Plugin.wrap方法:代理生成的工廠
Plugin.wrap()
是插件機制中的另一個關鍵組件,它負責創建動態代理對象:
?public class Plugin implements InvocationHandler {private final Object target; ? ? ? ? ? ? ? ? // 原始對象private final Interceptor interceptor; ? ? ? // 插件實例private final Map<Class<?>, Set<Method>> signatureMap; // 方法簽名映射public static Object wrap(Object target, Interceptor interceptor) {// 獲取插件聲明的攔截點Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();// 獲取目標對象實現的所有需要被攔截的接口Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {// 創建動態代理return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 檢查當前方法是否需要被攔截Set<Method> methods = signatureMap.get(method.getDeclaringClass());if (methods != null && methods.contains(method)) {// 如果需要攔截,調用插件的intercept方法return interceptor.intercept(new Invocation(target, method, args));}// 否則直接調用原始方法return method.invoke(target, args);}}
Plugin.wrap()
的智能之處在于它只會為那些實現了插件聲明要攔截的接口的對象創建代理,避免了不必要的性能開銷。
五、插件如何修改參數和返回值
基于上述架構,插件可以很容易地修改方法參數和返回值:
1. 修改方法參數:
?@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 獲取原始參數Object[] args = invocation.getArgs();// 修改參數(例如加密參數)if (args[0] instanceof String) {args[0] = encrypt((String) args[0]);}// 重要:將修改后的參數設置回Invocationinvocation.setArgs(args);// 繼續執行調用鏈return invocation.proceed();}
2. 修改返回值:
?@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 執行原有邏輯并獲取返回值Object result = invocation.proceed();// 修改返回值(例如解密結果)if (result instanceof String) {result = decrypt((String) result);}return result;}
3. 完全替換方法邏輯:
?@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 不調用proceed,完全由插件實現方法邏輯if (shouldReplaceLogic(invocation)) {return customLogic(invocation);}// 否則正常執行原有邏輯return invocation.proceed();}
六、總結:鏈式調用的設計哲學
MyBatis插件鏈式調用機制的設計體現了多個重要的軟件設計原則:
-
開閉原則:通過插件機制,可以在不修改框架源碼的情況下擴展功能。
-
單一職責原則:每個插件只關注一個特定的功能點。
-
依賴倒置原則:插件依賴于抽象的
Interceptor
接口,而不是具體的實現。 -
信息專家模式:
Invocation
對象集中管理了調用相關的所有信息。
這種設計不僅使得MyBatis插件系統極其強大和靈活,也為我們提供了如何設計可擴展架構的寶貴范例。無論是開發框架還是業務系統,這種責任鏈模式和鏈式調用的思想都值得深入學習和應用。
💖學習知識需費心,
📕整理歸納更費神。
🎉源碼免費人人喜,
🔥碼農福利等你領!💖常來我家多看看,
📕我是程序員扣棣,
🎉感謝支持常陪伴,
🔥點贊關注別忘記!💖山高路遠坑又深,
📕大軍縱橫任馳奔,
🎉誰敢橫刀立馬行?
🔥唯有點贊+關注成!