1. 程式人生 > >雙親委派模型和執行緒上下文類載入器

雙親委派模型和執行緒上下文類載入器

最近在讀《架構探險-從零開始寫java web框架》一書時,看到了一個獲取類載入器的地方是這樣寫的:

     
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
/**
     * 載入類
     */
    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cla = null;
        try {
            cla = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cla;
    }
感覺很奇怪,在的印象裡,類載入器只有三種,直接通過本類拿到一個classloader就好了,為什麼要獲取當前執行緒的classloader?

Jvm在執行時會產生3個類載入器:

根載入器:由C++編寫,在java中不可訪問。負責裝載JRE核心類庫。

ExtClassLoader擴充套件類載入器:繼承根載入器。負責裝載JRE的ext目錄下的擴充套件類包。

AppClassLoader應用類載入器:繼承擴充套件類載入器。負責裝載ClassPath下的jar包。

類在載入的過程中是遵循全盤負責/委託機制的。

1.全盤負責指使用一個ClassLoader載入類時,除非顯示地呼叫另一個ClassLoader

,否則該類所依賴引用的其他由該classLoader載入。

2.委託機制即雙親委派模型,指載入一個類時始終先從父類的父類的父類...直到根裝載器(bootstrapClassLoader)尋找目標類,如果啟動類載入器沒有發現無法載入,便將其交給子類載入,如果子類的子類載入器也無法載入,便會丟擲ClassNotFound異常。而且由於雙親委派模型,應用類裝載器可以看到啟動類裝載器所裝載的所有的類,而啟動類裝載器卻看不到應用類裝載器所裝載的類,有點像spring和springMVC的父子容器的bean關係吧?

雙親委派模型不是jvm強制使用的,而是推薦使用的類載入模式,可以保護jre核心類庫不被混淆。但這也產生了一些問題。比如在遠端方法呼叫(RMI)中實現 Java 序列化的時候,在從流中反序列化資料為 Java 物件時需要應用的類的相關資訊,但是反序列化程式碼屬於jre的核心類庫,由啟動裝載器所裝載,而反序列化的java物件則由應用類載入器載入;根據雙親委派模型它也就無法被反序列化的程式碼訪問到。

不僅僅是反序列化會出現問題,在基礎類中回撥使用者程式碼的操作都會帶開這些問題。Java提供了很多服務提供者介面(SPIService Provider Interface),允許獨立廠商(第三方)為此提供實現。常見的SPI有:JNDIJDBCJAXP等。這些介面由Java的核心庫來提供,所以問題就在於,SPI的介面是Java核心庫的一部分,它們是由啟動類載入器來載入的。SPI實現的Java類一般是由應用程式類載入器(Application ClassLoader)來載入的。啟動類無法找到SPI的實現類,因為它只加載核心庫(SPI的實現類由第三方提供)。它也不能代理給應用程式類載入器,因為它又是應用程式類載入器的父類,雙親委派模型又會將它交給啟動類來載入。所以在這個時候我們就要“打破”這個“雙親委派模型”。

這個時候執行緒上下文類載入器就油然而生,它是一個使用ThreadLocal裝載classloader的類載入器.

執行緒上下文類載入器(context class loader)是從 JDK 1.2 開始引入的。類 java.lang.Thread中的方法 getContextClassLoader() setContextClassLoader(ClassLoader cl)用來獲取和設定執行緒的上下文類載入器。如果沒有通過 setContextClassLoader(ClassLoader cl)方法進行設定的話,執行緒將繼承其父執行緒的上下文類載入器。Java應用執行的初始執行緒的上下文類載入器是系統類載入器。線上程中執行的程式碼可以通過此類載入器來載入類和資源。

它打破了雙親委派模型,直接通過執行緒上下文類載入器中的類載入器裝載指定類。一般來說,在需要動態載入類時,使用執行緒上下文類載入器,特別適用於開發框架。