1. 程式人生 > >Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例

Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例

【正文】Java類載入器(  CLassLoader ) 死磕9: 

上下文載入器原理和案例

本小節目錄


9.1. 父載入器不能訪問子載入器的類
9.2. 一個寵物工廠介面
9.3. 一個寵物工廠管理類
9.4 APPClassLoader不能訪問子載入器中的類
9.5. 執行緒上下文類載入器


類載入器的代理模式並不能解決 Java 應用開發中會遇到的類載入器的全部問題。Java 提供了很多服務提供者介面(Service Provider Interface,SPI),允許第三方為這些介面提供實現。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的介面由 Java 核心庫來提供,如 JAXP 的 SPI 介面定義包含在 javax.xml.parsers包中。這些 SPI 的實現程式碼很可能是作為 Java 應用所依賴的 jar 包被包含進來,可以通過類路徑(CLASSPATH)來找到,如實現了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 介面中的程式碼經常需要載入具體的實現類。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory類中的 newInstance()方法用來生成一個新的 DocumentBuilderFactory的例項。這裡的例項的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實現所提供的。如在 Apache Xerces 中,實現的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而問題在於,SPI 的介面是 Java 核心庫的一部分,是由引導類載入器來載入的;SPI 實現的 Java 類一般是由系統類載入器來載入的。引導類載入器是無法找到 SPI 的實現類的,因為它只加載 Java 的核心庫。它也不能代理給系統類載入器,因為它是系統類載入器的祖先類載入器。也就是說,類載入器的代理模式無法解決這個問題。

執行緒上下文類載入器(context class loader)是從 JDK 1.2 開始引入的。

在類java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設定執行緒的上下文類載入器。

如果沒有通過 setContextClassLoader(ClassLoader cl)方法進行設定的話,執行緒將繼承其父執行緒的上下文類載入器。Java 應用執行的初始執行緒的上下文類載入器是AppClassLoader類載入器。

執行緒上下文類載入器正好解決了這個問題。如果不做任何的設定,Java 應用的執行緒的上下文類載入器預設就是系統上下文類載入器。在 SPI 介面的程式碼中使用執行緒上下文類載入器,就可以成功的載入到 SPI 實現的類。執行緒上下文類載入器在很多 SPI 的實現中都會用到。


1.1.1. 父載入器不能訪問子載入器的類


關於父載入器,不能訪問子載入器載入的類,是一個比較大的問題,舉個栗子。


1.1.2. 一個寵物工廠介面


這裡有一個寵物工廠介面:

face IPetFactory {

    /**

     * 根據型別,構造一個物件

     * @param type 型別,如 Type.CAT

     * @return

     */

    IPet buildePet(IPet.Type type);

}


1.1.3. 一個寵物工廠管理類


還有一個寵物工廠管理類,根據類名,載入工廠實現類。

public class FactoryManager

{

    /**

     * 單例的Pet例項工廠

     */

    private static IPetFactory petFactory;

    /**

     * 已經類名,取得工廠例項

     *

     * @param factoryClassName

     * @return

     */

    public static IPetFactory getInstance(String factoryClassName)

    {

        if (null != petFactory)

        {

            return petFactory;

        }

        if (null == factoryClassName)

        {

            //預設的實現

            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; } }

1.1.4. APPClassLoader不能訪問子載入器中的類


先上程式碼:

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();

    }

}

執行上面的栗子,試一試。

發現,是失敗的。

原因是,FactoryManager是APPClassLoader載入的,而第三方的工廠類是FileClassLoader 載入的。

FileClassLoader 是APPClassLoader的子載入器。子載入器可以訪問父載入器中的類,但是父親不能訪問子載入器中的類。

所以,FactoryManager沒有辦法load子載入器中的工廠實現類。

1.1.5. 執行緒上下文類載入器


使用執行緒上下文類載入器,可以在執行執行緒中拋棄雙親委派載入鏈模式,使用執行緒上下文裡的類載入器載入類。

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設定成為執行緒上下文載入器,然後,在工廠管理器中,使用執行緒上限載入器載入:


public class FactoryManager

{

    /**

     * 單例的Pet例項工廠

     */

    private static IPetFactory petFactory;

    /**

     * 已經類名,取得工廠例項

     *

     * @param factoryClassName

     * @return

     */

    public static IPetFactory getInstance(String factoryClassName)

    {

        if (null != petFactory)
        {
            return petFactory;
        }

        if (null == factoryClassName)
        {
            //預設的實現
           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

下載地址:在瘋狂創客圈QQ群檔案共享。


瘋狂創客圈:如果說Java是一個武林,這裡的聚集一群武痴, 交流程式設計體驗心得
QQ群連結:
瘋狂創客圈QQ群


無程式設計不創客,無案例不學習。 一定記得去跑一跑案例哦


類載入器系列全目錄

1.匯入

2. JAVA類載入器分類

3. 揭祕ClassLoader抽象基類

4. 神祕的雙親委託機制

5. 入門案例:自定義一個檔案系統的自定義classLoader

6. 基礎案例:自定義一個網路類載入器

7. 中級案例:設計一個加密的自定義網路載入器

8. 高階案例1:使用ASM技術,結合類載入器,解密AOP原理

9. 高階案例2:上下文載入器原理和案例