一種解決jar包衝突的實現:隔離機制

業務背景

隨著業務的發展 和 架構的升級, 業務會越來越多的依賴公司內部提供的 中間件 ,如 rpc服務框架、分庫分表框架、異步消息框架、公共工具包等等。

每個中間件都有自己的 jar包依賴體系,最常用的如: logback、log4j、httpclient 、common-lang 、guava、zookeeper 等等 ,

這些jar包依賴不僅會產生版本衝突,甚至會有jar包不兼容的情況出現,比如 log4j 和 logback , guava 和 common-collection ,等等。

隨著時間推移,業務越來越複雜,依賴的中間件也越來越多,每當引入新的中間件時,jar包版本管理的風險也越來越大,甚至出現無法兼容的情況。

一種解決jar包衝突的實現:隔離機制

解決方案

中間件容器與 業務web容器隔離,jar包依賴互不影響。 不僅解決了令人頭痛的jar包衝突、jar包不兼容問題,還釋放了業務開發人員的壓力和責任,

公司內部的中間件通過專門的運維人員來推動升級,將業務與中間件隔離,通過透明的方式提供服務。

實現原理

先來回顧一下jvm的classloader加載機制。

當JVM啟動時,會形成由三個類加載器組成的初始類加載器層次結構:

bootstrap classloader

|

extension classloader

|

system classloader

bootstrap classloader -引導(也稱為原始)類加載器,它負責加載Java的核心類。

在執行java的命令中使用-Xbootclasspath選項或使用 - D選項指定sun.boot.class.path系統屬性值可以指定附加的類。

這個加載器的是非常特殊的,它實際上不是 java.lang.ClassLoader的子類,而是由JVM自身實現的。

可以通過下面的代碼來查看原始類加載器加載了那些jar包, 它主要負責加載 JAVA_HOME/lib/*.jar

URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls.toExternalform());
}

extension classloader -擴展類加載器,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統屬性指定的)中JAR的類包。

默認的擴展目錄對所有從同一個JRE中啟動的JVM都是通用的,所以放入這個目錄的 JAR類包對所有的JVM和system classloader都是可見的。

臨時解決jar衝突的終極大招:將jar包拷貝到ext目錄下。

在這個實例上調用方法getParent()總是返回空值null,因為引導加載器bootstrap classloader不是一個真正的ClassLoader實例。

system classloader -系統類加載器,它負責在JVM被啟動時,加載來自在命令java中的-classpath或者java.class.path系統屬性

或者 CLASSPATH*作系統屬性所指定的JAR類包和類路徑。

總能通過靜態方法ClassLoader.getSystemClassLoader()找到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。

重點

classloader 加載類用的是全盤負責、委託機制。

所謂全盤負責,即是當一個classloader加載一個Class的時候,這個Class所依賴的和引用的所有 Class也由這個classloader負責載入,除非是顯式的使用另外一個classloader載入;

委託機制則是先讓parent(父)類加載器 (而不是super,它與parent classloader類不是繼承關係)尋找,只有在parent找不到的時候才從自己的類路徑中去尋找。


分享到:


相關文章: