首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 類加載器深度解析:雙親委派、自定義加載、Web 容器隔離與隱式顯式加載

     編輯:Java 相關知識

1. 類加載器核心職責

  • 讀取 .class 字節碼(文件、網絡、動態生成)。
  • 轉換為 java.lang.Class 對象(方法區存儲元數據,堆創建 Class 實例)。
  • 保護系統命名空間:防止惡意代碼偽裝核心類(如 java.lang.String)。

2. 雙親委派模型與層級結構

類加載器層級圖

Bootstrap ClassLoader (C++ 實現,無 Java 對象)
    ↑ 父
Extension ClassLoader (sun.misc.Launcher$ExtClassLoader)
    ↑ 父
System/Application ClassLoader (sun.misc.Launcher$AppClassLoader)  ← 類路徑
    ↑ 父
Custom ClassLoader (用戶自定義)

加載流程

  1. 檢查本地緩存(已加載直接返回)。
  2. 委派父加載器遞歸嘗試加載。
  3. 父加載失敗(拋 ClassNotFoundException),子加載器調用 findClass() 自行加載。

優勢:避免重複加載、保護核心類不被篡改(如用戶自定義 java.lang.String 永不會被加載,因 Bootstrap 優先找到系統類)。

代碼驗證層級


ClassLoader cl = ClassLoader.getSystemClassLoader();
while (cl != null) {
    System.out.println(cl);
    System.out.println("parent: " + cl.getParent());
    cl = cl.getParent();
}
// 輸出:
// sun.misc.Launcher$AppClassLoader@19821f
// parent: sun.misc.Launcher$ExtClassLoader@addbf1
// sun.misc.Launcher$ExtClassLoader@addbf1
// parent: null  ← 代表 Bootstrap

3. 自定義類加載器實現

核心步驟

  1. 繼承 ClassLoader。
  2. 覆寫 findClass(String name):獲取字節碼 → defineClass() 轉換為 Class 對象。

public class MyClassLoader extends ClassLoader {
    private String basePath; // 字節碼來源路徑/URL

    public MyClassLoader(String basePath) { this.basePath = basePath; }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name); // 從文件/網絡/DB 讀取 byte[]
        if (classData == null) throw new ClassNotFoundException(name);
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] loadClassData(String name) {
        // 實現:讀取 {basePath}/{name}.class 文件或 HTTP 請求
        // 返回 byte[] 或 null
    }
}

關鍵方法

  • getResourceAsStream(name) / getResource(name):獲取資源流/URL。
  • defineClass(name, byte[], off, len, ProtectionDomain):字節碼 → Class 對象(final,不可覆寫)。
  • resolveClass(Class):鏈接階段(驗證、準備、解析),loadClass(..., true) 會自動調用。

同一字節碼,不同加載器 = 不同類


URL[] urls = {new URL("file:/c:/")};
URLClassLoader ucl1 = new URLClassLoader(urls);
URLClassLoader ucl2 = new URLClassLoader(urls);
Class c1 = ucl1.loadClass("Foo");
Class c2 = ucl2.loadClass("Foo");
System.out.println(c1 == c2); // false
c1.newInstance(); // counter: 1
c2.newInstance(); // counter: 1 (各自獨立靜態初始化)

這是熱部署、模塊化隔離(OSGi、Spring Boot DevTools、Tomcat 熱加載)的理論基礎。

4. Web 容器類加載器隔離

  • Common ClassLoader:容器共享庫。
  • Webapp ClassLoader:每個 Web 應用獨立一個,加載 WEB-INF/classes、WEB-INF/lib。**優先自行加載(打破雙親委派)**,實現應用間類隔離、版本衝突解耦。
  • JSP ClassLoader:每個 JSP 文件獨立加載器,支持 JSP 修改後熱編譯熱加載(舊實例自然 GC,新實例新加載器加載)。

5. 隱式加載 vs 顯式加載

場景:JDBC 驅動註冊需初始化(Class.forName("com.mysql.Driver"));框架掃描註解僅需元數據(loadClass 不初始化,性能更好)。

6. 兩大異常區別

  • NoClassDefFoundError (Error):編譯期存在 .class,**運行期**類路徑找不到(如依賴包缺失、靜態初始化拋異常導致類加載失敗)。
  • ClassNotFoundException (Exception):顯式加載(forName/loadClass)時類路徑找不到。

總結

類加載器是 Java 動態性的基石:雙親委派保安全、自定義加載器實現隔離/熱加載、Web 容器多層級隔離、隱式/顯式加載控制初始化時機。深入理解它,能解決類衝突、熱部署、框架原理(Spring Boot DevTools、OSGi、ShardingSphere)等高難度問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方式觸發時機是否初始化
隱式 new Xxx()首次主動使用類是(執行 <clinit>)
顯式 Class.forName("Xxx")調用時默認是(forName0(name, true, callerCL))
顯式 Class.forName("Xxx", false, loader)調用時否(僅加載鏈接,不執行靜態初始化)
顯式 loader.loadClass("Xxx")調用時否(同 forName(..., false))