您好,登錄后才能下訂單哦!
【正文】Java類加載器(? CLassLoader ) 死磕9:?
上下文加載器原理和案例
9.1. 父加載器不能訪問子加載器的類
9.2. 一個(gè)寵物工廠接口
9.3. 一個(gè)寵物工廠管理類
9.4 APPClassLoader不能訪問子加載器中的類
9.5. 線程上下文類加載器
類加載器的代理模式并不能解決 Java 應(yīng)用開發(fā)中會(huì)遇到的類加載器的全部問題。Java 提供了很多服務(wù)提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實(shí)現(xiàn)。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定義包含在 javax.xml.parsers包中。這些 SPI 的實(shí)現(xiàn)代碼很可能是作為 Java 應(yīng)用所依賴的 jar 包被包含進(jìn)來,可以通過類路徑(CLASSPATH)來找到,如實(shí)現(xiàn)了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代碼經(jīng)常需要加載具體的實(shí)現(xiàn)類。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory類中的 newInstance()方法用來生成一個(gè)新的 DocumentBuilderFactory的實(shí)例。這里的實(shí)例的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實(shí)現(xiàn)所提供的。如在 Apache Xerces 中,實(shí)現(xiàn)的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而問題在于,SPI 的接口是 Java 核心庫的一部分,是由引導(dǎo)類加載器來加載的;SPI 實(shí)現(xiàn)的 Java 類一般是由系統(tǒng)類加載器來加載的。引導(dǎo)類加載器是無法找到 SPI 的實(shí)現(xiàn)類的,因?yàn)樗患虞d Java 的核心庫。它也不能代理給系統(tǒng)類加載器,因?yàn)樗窍到y(tǒng)類加載器的祖先類加載器。也就是說,類加載器的代理模式無法解決這個(gè)問題。
線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的。
在類java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設(shè)置線程的上下文類加載器。
如果沒有通過 setContextClassLoader(ClassLoader cl)方法進(jìn)行設(shè)置的話,線程將繼承其父線程的上下文類加載器。Java 應(yīng)用運(yùn)行的初始線程的上下文類加載器是AppClassLoader類加載器。
線程上下文類加載器正好解決了這個(gè)問題。如果不做任何的設(shè)置,Java 應(yīng)用的線程的上下文類加載器默認(rèn)就是系統(tǒng)上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實(shí)現(xiàn)的類。線程上下文類加載器在很多 SPI 的實(shí)現(xiàn)中都會(huì)用到。
關(guān)于父加載器,不能訪問子加載器加載的類,是一個(gè)比較大的問題,舉個(gè)栗子。
這里有一個(gè)寵物工廠接口:
face IPetFactory { /** * 根據(jù)類型,構(gòu)造一個(gè)對象 * @param type 類型,如 Type.CAT * @return */ IPet buildePet(IPet.Type type); }
還有一個(gè)寵物工廠管理類,根據(jù)類名,加載工廠實(shí)現(xiàn)類。
public class FactoryManager { /** * 單例的Pet實(shí)例工廠 */ private static IPetFactory petFactory; /** * 已經(jīng)類名,取得工廠實(shí)例 * * @param factoryClassName * @return */ public static IPetFactory getInstance(String factoryClassName) { if (null != petFactory) { return petFactory; } if (null == factoryClassName) { //默認(rèn)的實(shí)現(xiàn) factoryClassName = "com.crazymakercircle.petStore.pet.factory.PetFactoryImpl"; } /** * 獲得線程上下文加載器 */ ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { // Class<?> factoryClass = loader.loadClass(factoryClassName); Class<?> factoryClass=Class.forName(factoryClassName); petFactory = (IPetFactory) factoryClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } return petFactory; } }
先上代碼:
public class FailCase { private static IPetFactory petFactory=null; public static void testLoader() { try { String baseDir = SystemConfig.PET_LIB_PATH; FileClassLoader classLoader = new FileClassLoader(baseDir); String className = SystemConfig.PET_FACTORY_CLASS; Class petFactoryClass = classLoader.loadClass(className); Logger.info("顯示petFactoryClass 的ClassLoader tree:"); ClassLoaderUtil.showLoader4Class(petFactoryClass); petFactory = FactoryManager.getInstance(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void showPet() { IPet dog=petFactory.buildePet(IPet.Type.DOG); dog.sayHello(); } public static void main(String[] args) { testLoader(); showPet(); } }
運(yùn)行上面的栗子,試一試。
發(fā)現(xiàn),是失敗的。
原因是,F(xiàn)actoryManager是APPClassLoader加載的,而第三方的工廠類是FileClassLoader 加載的。
FileClassLoader 是APPClassLoader的子加載器。子加載器可以訪問父加載器中的類,但是父親不能訪問子加載器中的類。
所以,F(xiàn)actoryManager沒有辦法load子加載器中的工廠實(shí)現(xiàn)類。
使用線程上下文類加載器,可以在執(zhí)行線程中拋棄雙親委派加載鏈模式,使用線程上下文里的類加載器加載類。
public class CorrectCase { private static IPetFactory petFactory=null; public static void testLoader() { try { String baseDir = SystemConfig.PET_LIB_PATH; FileClassLoader classLoader = new FileClassLoader(baseDir); Thread.currentThread().setContextClassLoader(classLoader); String className = SystemConfig.PET_FACTORY_CLASS; petFactory = FactoryManager.getInstance(className); Class petFactoryClass = classLoader.loadClass(className); Logger.info("顯示petFactoryClass 的ClassLoader tree:"); ClassLoaderUtil.showLoader4Class(petFactoryClass); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void showPet() { IPet dog=petFactory.buildePet(IPet.Type.DOG); dog.sayHello(); } public static void main(String[] args) { testLoader(); showPet(); } }
將FileClassLoader設(shè)置成為線程上下文加載器,然后,在工廠管理器中,使用線程上限加載器加載:
public class FactoryManager
{
/**
* 單例的Pet實(shí)例工廠
*/
private static IPetFactory petFactory;
/**
* 已經(jīng)類名,取得工廠實(shí)例
*
* @param factoryClassName
* @return
*/
public static IPetFactory getInstance(String factoryClassName)
{
if (null != petFactory)
{
return petFactory;
}
if (null == factoryClassName)
{
//默認(rèn)的實(shí)現(xiàn)
factoryClassName ="com.crazymakercircle.petStore.pet.factory.PetFactoryImpl";
}
/**
* 獲得線程上下文加載器
*/
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try
{
Class<?> factoryClass = loader.loadClass(factoryClassName);
petFactory = (IPetFactory) factoryClass.newInstance();
} catch (Exception e)
{
e.printStackTrace();
}
return petFactory;
}
}
代碼工程:? classLoaderDemo.zip
下載地址:在瘋狂創(chuàng)客圈QQ群文件共享。
瘋狂創(chuàng)客圈:如果說Java是一個(gè)武林,這里的聚集一群武癡, 交流編程體驗(yàn)心得
QQ群鏈接:瘋狂創(chuàng)客圈QQ群
類加載器系列全目錄
1.導(dǎo)入
2. JAVA類加載器分類
3. 揭秘ClassLoader抽象基類
4. 神秘的雙親委托機(jī)制
5. 入門案例:自定義一個(gè)文件系統(tǒng)的自定義classLoader
6. 基礎(chǔ)案例:自定義一個(gè)網(wǎng)絡(luò)類加載器
7. 中級案例:設(shè)計(jì)一個(gè)加密的自定義網(wǎng)絡(luò)加載器
8. 高級案例1:使用ASM技術(shù),結(jié)合類加載器,解密AOP原理
9. 高級案例2:上下文加載器原理和案例
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。