業務背景
隨著業務的發展 和 架構的升級, 業務會越來越多的依賴公司內部提供的 中間件 ,如 rpc服務框架、分庫分表框架、異步消息框架、公共工具包等等。
每個中間件都有自己的 jar包依賴體系,最常用的如: logback、log4j、httpclient 、common-lang 、guava、zookeeper 等等 ,
這些jar包依賴不僅會產生版本沖突,甚至會有jar包不兼容的情況出現,比如 log4j 和 logback , guava 和 common-collection ,等等。
隨著時間推移,業務越來越復雜,依賴的中間件也越來越多,每當引入新的中間件時,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找不到的時候才從自己的類路徑中去尋找。