在項目維護過程中,我們可能會遇到歷史項目依賴的第三方庫出現BUG而需要修復的情況,而這些第三方庫可能來源于公司自主開發或開源項目,但由于各種原因,這些庫可能已無人維護。
此時,解決這個問題有三個辦法
1、基于源碼修改重新打包發布
2、某些時候可能沒有源碼,需要反編譯進行修改再重新編譯打包
3、類覆蓋
本文主要分享第三種類覆蓋的實現方式,用于在一些特殊情況下解決線上問題,保證項目的正常運行。
一、類覆蓋的基本原理
原理如其名,通過自定義類覆蓋目標類。
具體實現方式為通過定義與三方庫中存在問題目標類同名的自定義類,并確保其加載優先級高于原始類。
這里特別說明下類加載優先級的控制,分兩種情況
1. 直接依賴
項目直接依賴了一個庫C,項目源碼打包后是在classes目錄下的,此種情況無需特殊控制,默認classes目錄優先加載,因此自定義的類會優先于依賴庫中的類加載。
2. 關聯依賴
項目依賴了庫B,庫B又依賴了庫C,如果要控制庫B和庫C中同名類的加載順序,可以通過定義依賴順序進行控制。
以SpringBoot Maven 工程為例,B依賴定義在C依賴上面,可以保證最終生成的可執行jar中classpath.idx里面定義的庫加載順序B優先于C,保證最終加載時B庫中的類優先于C庫加載。
<dependency><groupId>org.example</groupId><artifactId>B</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
<dependency><groupId>org.example</groupId><artifactId>C</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
二、一個示例
如下工程,有一個依賴的三方庫C
C依賴庫種有一個C工具類,代碼如下,其中的isEmpty方法未判斷null值,會導致空指針異常
package org.c;public class C {public static boolean isEmpty(String s){return s.isEmpty();}}
主模塊A類代碼如下,調用了C類的isEmpty方法
public class A {public static void main(String[] args ) {String s = "123";if(!C.isEmpty(s)){System.out.println("String is " + s);}String ss = null;if(!C.isEmpty(ss)){ // 異常位置System.out.println("String is " + s);}}}
運行A類將會在異常位置處出現空指針異常。
下面使用類覆蓋方法進行BUG修正。
2.1 創建同名類
在A模塊種創建同名類C,如下
注意同名類是整個包名 + 類名相同
自定義C類的代碼如下
package org.c;public class C {public static boolean isEmpty(String s){// 修復原始庫空指針問題if(s == null) {return true;}return s.isEmpty();}}
2.2 驗證效果
再次運行A類,程序正常執行完成。
三、總結
首先,優先推薦前兩種方式進行問題修復,但是某些時候受制于時間、成本、風險等因素需要快速解決問題,這時,可以考慮第三種方案類覆蓋。
需要特別注意的是,修改的方法可能會被多處調用,修改時需要充分考慮兼容性。