業(yè)務(wù)背景
隨著業(yè)務(wù)的發(fā)展 和 架構(gòu)的升級(jí), 業(yè)務(wù)會(huì)越來越多的依賴公司內(nèi)部提供的 中間件 ,如 rpc服務(wù)框架、分庫(kù)分表框架、異步消息框架、公共工具包等等。
每個(gè)中間件都有自己的 jar包依賴體系,最常用的如: logback、log4j、httpclient 、common-lang 、guava、zookeeper 等等 ,
這些jar包依賴不僅會(huì)產(chǎn)生版本沖突,甚至?xí)衘ar包不兼容的情況出現(xiàn),比如 log4j 和 logback , guava 和 common-collection ,等等。
隨著時(shí)間推移,業(yè)務(wù)越來越復(fù)雜,依賴的中間件也越來越多,每當(dāng)引入新的中間件時(shí),jar包版本管理的風(fēng)險(xiǎn)也越來越大,甚至出現(xiàn)無法兼容的情況。
解決方案
中間件容器與 業(yè)務(wù)web容器隔離,jar包依賴互不影響。 不僅解決了令人頭痛的jar包沖突、jar包不兼容問題,還釋放了業(yè)務(wù)開發(fā)人員的壓力和責(zé)任,
公司內(nèi)部的中間件通過專門的運(yùn)維人員來推動(dòng)升級(jí),將業(yè)務(wù)與中間件隔離,通過透明的方式提供服務(wù)。
實(shí)現(xiàn)原理
先來回顧一下jvm的classloader加載機(jī)制。
當(dāng)JVM啟動(dòng)時(shí),會(huì)形成由三個(gè)類加載器組成的初始類加載器層次結(jié)構(gòu):
bootstrap classloader
|
extension classloader
|
system classloader
bootstrap classloader -引導(dǎo)(也稱為原始)類加載器,它負(fù)責(zé)加載JAVA的核心類。
在執(zhí)行java的命令中使用-Xbootclasspath選項(xiàng)或使用 - D選項(xiàng)指定sun.boot.class.path系統(tǒng)屬性值可以指定附加的類。
這個(gè)加載器的是非常特殊的,它實(shí)際上不是 java.lang.ClassLoader的子類,而是由JVM自身實(shí)現(xiàn)的。
可以通過下面的代碼來查看原始類加載器加載了那些jar包, 它主要負(fù)責(zé)加載 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 -擴(kuò)展類加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統(tǒng)屬性指定的)中JAR的類包。
默認(rèn)的擴(kuò)展目錄對(duì)所有從同一個(gè)JRE中啟動(dòng)的JVM都是通用的,所以放入這個(gè)目錄的 JAR類包對(duì)所有的JVM和system classloader都是可見的。
臨時(shí)解決jar沖突的終極大招:將jar包拷貝到ext目錄下。
在這個(gè)實(shí)例上調(diào)用方法getParent()總是返回空值null,因?yàn)橐龑?dǎo)加載器bootstrap classloader不是一個(gè)真正的ClassLoader實(shí)例。
system classloader -系統(tǒng)類加載器,它負(fù)責(zé)在JVM被啟動(dòng)時(shí),加載來自在命令java中的-classpath或者java.class.path系統(tǒng)屬性
或者 CLASSPATH*作系統(tǒng)屬性所指定的JAR類包和類路徑。
總能通過靜態(tài)方法ClassLoader.getSystemClassLoader()找到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。
重點(diǎn)
classloader 加載類用的是全盤負(fù)責(zé)、委托機(jī)制。
所謂全盤負(fù)責(zé),即是當(dāng)一個(gè)classloader加載一個(gè)Class的時(shí)候,這個(gè)Class所依賴的和引用的所有 Class也由這個(gè)classloader負(fù)責(zé)載入,除非是顯式的使用另外一個(gè)classloader載入;
委托機(jī)制則是先讓parent(父)類加載器 (而不是super,它與parent classloader類不是繼承關(guān)系)尋找,只有在parent找不到的時(shí)候才從自己的類路徑中去尋找。