下面來(lái)看一個(gè)例子,在該例子中,我們要完成的工作是利用URLClassLoader加載jar并運(yùn)行其中的類(lèi)的某個(gè)方法。
首先我們定義一個(gè)接口,使所有繼承它的類(lèi)都必須實(shí)現(xiàn)action方法,如下:





完成后將其打包為testInterface.jar文件。
接下來(lái)新建一工程,為了編譯通過(guò),引入之前打好的testInterface.jar包。并創(chuàng)建TestAction類(lèi),使它實(shí)現(xiàn)ActionInterface接口。如下:










完成后將其打包為test.jar,放在c盤(pán)根目錄下。下面要做的就是利用URLClassLoader加載并運(yùn)行TestAction的action方法,并將返回的值打印在控制臺(tái)上。
新建一工程,引入testInterface.jar包。并創(chuàng)建一可執(zhí)行類(lèi)(main方法),在其中加入如下代碼:







在上面的例子中,首先利用URLClassLoader加載了C:\test.jar包,將其中的com.mxjava.TestAction類(lèi)載入內(nèi)存,將其強(qiáng)制轉(zhuǎn)型為testInterface包中的ActionInterface類(lèi)型,最后調(diào)用其action方法,并打印到控制臺(tái)中。
執(zhí)行程序后,在控制臺(tái)上如期打印出我們想要的內(nèi)容。但是,事情并沒(méi)有那么簡(jiǎn)單,當(dāng)我們將該代碼移動(dòng)web應(yīng)用中時(shí),就會(huì)拋出異常。原來(lái),Java為我們提供了三種可選擇的ClassLoader:
1. 系統(tǒng)類(lèi)加載器或叫作應(yīng)用類(lèi)加載器 (system classloader or application classloader)
2. 當(dāng)前類(lèi)加載器
3. 當(dāng)前線程類(lèi)加載器
在上例中我們使用javac命令來(lái)運(yùn)行該程序,這時(shí)候使用的是系統(tǒng)類(lèi)加載器 (system classloader)。這個(gè)類(lèi)加載器處理 -classpath下的類(lèi)加載工作,可以通過(guò)ClassLoader.getSystemClassLoader()方法調(diào)用。 ClassLoader 下所有的 getSystemXXX()的靜態(tài)方法都是通過(guò)這個(gè)方法定義的。在代碼中,應(yīng)該盡量少地調(diào)用這個(gè)方法,以其它的類(lèi)加載器作為代理。否則代碼將只能工作在簡(jiǎn)單的命令行應(yīng)用中。
當(dāng)在web應(yīng)用中時(shí),服務(wù)器也是利用ClassLoader來(lái)加載class的,由于ClassLoader的不同,所以在強(qiáng)制轉(zhuǎn)型時(shí)JVM認(rèn)定不是同一類(lèi)型。(在JAVA中,一個(gè)類(lèi)用其完全匹配類(lèi)名(fully qualified class name)作為標(biāo)識(shí),這里指的完全匹配類(lèi)名包括包名和類(lèi)名。但在JVM中一個(gè)類(lèi)用其全名和一個(gè)加載類(lèi)ClassLoader的實(shí)例作為唯一標(biāo)識(shí)。因此,如果一個(gè)名為Pg的包中,有一個(gè)名為Cl的類(lèi),被類(lèi)加載器KlassLoader的一個(gè)實(shí)例kl1加載,Cl的實(shí)例,即C1.class在JVM中表示為(Cl, Pg, kl1)。這意味著兩個(gè)類(lèi)加載器的實(shí)例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它們所加載的類(lèi)也因此完全不同,互不兼容的。)為了能夠使程序正確運(yùn)行,我們首要解決的問(wèn)題就是,如何將URLClassLoader加載的類(lèi),同當(dāng)前ClassLoader保持在同一類(lèi)加載器中。解決方法很簡(jiǎn)單,利用java提供的第三種ClassLoader—當(dāng)前線程類(lèi)加載器即可。jdk api文檔就會(huì)發(fā)現(xiàn),URLClassLoader提供了三種構(gòu)造方式:






接下來(lái)要做的就是,在構(gòu)造URLClassLoader時(shí),將當(dāng)前線程類(lèi)加載器置入即可。如下:



總結(jié):
Java是利用ClassLoader來(lái)加載類(lèi)到內(nèi)存的,ClassLoader本身是用java語(yǔ)言寫(xiě)的,所以我們可以擴(kuò)展自己的ClassLoader。利用URLClassLoader可以加載指定jar包中的類(lèi)到內(nèi)存。在命行上利用URLClassLoader加載jar時(shí),是使用系統(tǒng)類(lèi)加載器來(lái)加載class的,所以在web環(huán)境下,就會(huì)出錯(cuò)。這是因?yàn)镴VM中一個(gè)類(lèi)用其全名和一個(gè)加載類(lèi)ClassLoader的實(shí)例作為唯一標(biāo)識(shí)的。我們只要利用URLClassLoader的第二種構(gòu)造方法并傳入當(dāng)前線程類(lèi)加載器即可解決。