classloader簡(jiǎn)介
ClassLoader的具體作用就是將class文件加載到j(luò)vm虛擬機(jī)中去割粮,程序就可以正確運(yùn)行了筹吐。但是杨伙,jvm啟動(dòng)的時(shí)候,并不會(huì)一次性加載所有的class文件传藏,而是根據(jù)需要去動(dòng)態(tài)加載。
classloader 有兩種裝載class的方式 (時(shí)機(jī)):
- 隱式:運(yùn)行過(guò)程中彤守,碰到new方式生成對(duì)象時(shí)漩氨,隱式調(diào)用classLoader到JVM
- 顯式:通過(guò)class.forname()動(dòng)態(tài)加載
類加載器的樹(shù)狀組織結(jié)構(gòu)
Java 中的類加載器大致可以分成兩類,一類是系統(tǒng)提供的遗增,另外一類則是由 Java 應(yīng)用開(kāi)發(fā)人員編寫(xiě)的叫惊。系統(tǒng)提供
的類加載器主要有下面三個(gè):
-引導(dǎo)類加載器(Bootstrap Classloader):它用來(lái)加載 Java 的核心庫(kù),是用原生代碼來(lái)實(shí)現(xiàn)的做修,并不繼承自 java.lang.ClassLoader 霍狰。
-擴(kuò)展類加載器(Extensions Classloader):它用來(lái)加載 Java 的擴(kuò)展庫(kù)抡草。Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄。該類加載器在此目錄里面查找并加載 Java 類蔗坯。
-系統(tǒng)類加載器(System Classloader):它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH)來(lái)加載 Java 類康震。一般來(lái)說(shuō),Java 應(yīng)用的類都是由它來(lái)完成加載的宾濒⊥榷蹋可以通過(guò) ClassLoader.getSystemClassLoader() 來(lái)獲取它。
除了系統(tǒng)提供的類加載器以外绘梦,開(kāi)發(fā)人員可以通過(guò)繼承 java.lang.ClassLoader 類的方式實(shí)現(xiàn)自己的類加載器橘忱,以滿足一些特殊的需求。
一個(gè)ClassLoader創(chuàng)建時(shí)如果沒(méi)有指定parent卸奉,那么它的parent默認(rèn)就是AppClassLoader钝诚。
每個(gè)Thread都有一個(gè)相關(guān)聯(lián)的ClassLoader,默認(rèn)是AppClassLoader榄棵。并且子線程默認(rèn)使用父線程的ClassLoader
除非子線程特別設(shè)置凝颇。
雙親委托
BootStrap Classloder
|
Extensions Classloader
|
System Classloader
/ \
Custom Classloader Webapp Classloader
一個(gè)類加載器查找class和resource時(shí),是通過(guò)“委托模式”進(jìn)行的疹鳄,它首先判斷這個(gè)class是不是已經(jīng)加載成功拧略,如果沒(méi)有的話它并不是自己進(jìn)行查找,而是先通過(guò)父加載器瘪弓,然后遞歸下去垫蛆,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了杠茬,直接返回月褥,如果沒(méi)有找到,則一級(jí)一級(jí)返回瓢喉,最后到達(dá)自身去查找這些對(duì)象宁赤。這種機(jī)制就叫做雙親委托
使用這種模型來(lái)組織類加載器之間的關(guān)系的好處: 主要是為了 安全性 ,避免用戶自己編寫(xiě)的類動(dòng)態(tài)替換 Java 的一些核心類栓票,比如 String决左,同時(shí)也避免了 重復(fù)加載 ,因?yàn)?JVM 中區(qū)分不同類走贪,不僅僅是根據(jù)類名佛猛,相同的 class 文件被不同的 ClassLoader 加載就是不同的兩個(gè)類,如果相互轉(zhuǎn)型的話會(huì)拋java.lang.ClassCaseException.
用序列描述一下:
- 一個(gè)AppClassLoader查找資源時(shí)坠狡,先看看緩存是否有继找,緩存有從緩存中獲取,否則委托給父加載器逃沿。
- 遞歸婴渡,重復(fù)第1部的操作幻锁。
- 如果ExtClassLoader也沒(méi)有加載過(guò),則由Bootstrap ClassLoader出面边臼,它首先查找緩存哄尔,如果沒(méi)有找到的
話,就去找自己的規(guī)定的路徑下柠并,也就是 sun.mic.boot.class 下面的路徑岭接。找到就返回,沒(méi)有找到臼予,讓子加
載器自己去找鸣戴。 - Bootstrap ClassLoader如果沒(méi)有查找成功,則ExtClassLoader自己在 java.ext.dirs 路徑中去查找瘟栖,查找成
功就返回葵擎,查找不成功谅阿,再向下讓子加載器找半哟。 - ExtClassLoader查找不成功,AppClassLoader就自己查找签餐,在 java.class.path 路徑下查找寓涨。找到就返回。
如果沒(méi)有找到就讓子類找氯檐,如果沒(méi)有子類會(huì)怎么樣戒良?拋出各種異常。
常見(jiàn)加載類錯(cuò)誤分析
ClassNotFoundExecption
ClassNotFoundExecption 異常是平常碰到的最多的冠摄。這個(gè)異常通常發(fā)生在顯示加載類的時(shí)候糯崎。
顯示加載一個(gè)類通常有:
-通過(guò)類 Class 中的 forName() 方法
-通過(guò)類 ClassLoader 中的 loadClass() 方法
-通過(guò)類 ClassLoader 中的 findSystemClass() 方法
出現(xiàn)這種錯(cuò)誤其實(shí)就是當(dāng) JVM 要加載指定文件的字節(jié)碼到內(nèi)存時(shí),并沒(méi)有找到這個(gè)文件對(duì)應(yīng)的字節(jié)碼河泳,也就是這個(gè)文件并不存在沃呢。解決方法就是檢查在當(dāng)前的 classpath 目錄下有沒(méi)有指定的文件。
NoClassDefFoundError
在JavaDoc中對(duì)NoClassDefFoundError的產(chǎn)生可能的情況就是使用new關(guān)鍵字拆挥、屬性引用某個(gè)類薄霜、繼承了某個(gè)接口或者類,以及方法的某個(gè)參數(shù)中引用了某個(gè)類纸兔,這時(shí)就會(huì)觸發(fā)JVM或者類加載器實(shí)例嘗試加載類型的定義惰瓜,但是該定義卻沒(méi)有找到,影響了執(zhí)行路徑汉矿。換句話說(shuō)崎坊,在編譯時(shí)這個(gè)類是能夠被找到的,但是在執(zhí)行時(shí)卻沒(méi)有找到洲拇。
解決這個(gè)錯(cuò)誤的方法就是確保每個(gè)類引用的類都在當(dāng)前的classpath下面奈揍。
ClassCastException
該錯(cuò)誤通常出現(xiàn)強(qiáng)制類型轉(zhuǎn)換時(shí)出現(xiàn)這個(gè)錯(cuò)誤痹届。
NoSuchMethodError
NoSuchMethodError代表這個(gè)類型確實(shí)存在,但是一個(gè)不正確的版本被加載了打月。
平臺(tái)類加載簡(jiǎn)介
我認(rèn)為有了上一節(jié)classloader的介紹后队腐,所謂的平臺(tái)的類加載原理其實(shí)就是classloader那部分內(nèi)容因?yàn)榻?jīng)常遇到j(luò)ar包沖突的問(wèn)題,小部分同事還經(jīng)常將jar包因?yàn)轭惣虞d不到或不對(duì)奏篙,嘗試性的將jar包放各種目錄柴淘,以提一下類加載是有先后順序的,有助于大家以后先定位問(wèn)題再解決問(wèn)題秘通,而不是嘗試解決后为严,還不清楚問(wèn)題原因的本質(zhì),避免將來(lái)重蹈覆轍肺稀。
平臺(tái)的類加載優(yōu)先級(jí)
lib/ > applib/ > app/applib/ > app/模塊/applib/ > ${HWORKDIR}/ > ${HWORKDIR}/app/
這里的模塊指的是${HWORKDIR}/etc/app.list中的模塊第股,一行一個(gè)模塊
至于weblib,其實(shí)它不是我們的類加載器指定的路徑话原,它真正被使用到的是webapp的lib/下通過(guò)軟連接引用的夕吻,由于webapp可能較多,所以用weblib一個(gè)公共存放管理的目錄可以很好的統(tǒng)一管理web類jar包
平臺(tái)自定義類加載器
package com.xxx.loader;
import java.net.URL;
import java.net.URLClassLoader;
public class XxStandardClassLoader extends URLClassLoader {
public XxStandardClassLoader(URL repositories[]) {
super(repositories);
}
public XxStandardClassLoader(URL repositories[], ClassLoader parent) {
super(repositories, parent);
}
}
每一個(gè)ATR服務(wù)解析成java對(duì)象時(shí)繁仁,都會(huì)創(chuàng)建其獨(dú)有的XxStandardClassLoader涉馅,也就是每一個(gè)ATR配置文件對(duì)應(yīng)的服務(wù),都有一個(gè)與其對(duì)應(yīng)的不同與其他服務(wù)的類加載器黄虱。這樣在每個(gè)線程執(zhí)行這些服務(wù)的Process方法之前稚矿,處理引擎負(fù)責(zé)將當(dāng)前線程的classloader替換為該服務(wù)的classloader,以便處理中的classloader為當(dāng)前服務(wù)的classloader捻浦,當(dāng)Process結(jié)束時(shí)晤揣,處理引擎又將classloader還原為之前的。這樣可以做到服務(wù)間的類隔離朱灿,再結(jié)合類加載的優(yōu)先順序昧识,還以做到同一個(gè)容器下,不同服務(wù)可以使用不同版本的類母剥,這種方式也可以解決類沖突帶來(lái)的問(wèn)題滞诺。