成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JVM 類加載器有哪些?雙親委派機制的作用是什么?如何自定義類加載器?

開發 前端
引導類加載器 BootstrapClassLoader:引導類加載器是使用 C++ 語言實現的,嵌入在 JVM 中。用于加載 Java 中的核心類庫的,不繼承自 java.lang.ClassLoader,在 Java 程序中通常返回 null。

類加載器分類

先回顧下,在 Java 中,類的初始化分為幾個階段: 加載、鏈接(包括驗證、準備和解析)和 初始化。

而 類加載器(Class Loader)則是加載階段中,負責將本地或網絡中的指定類的二進制流,加載到 Java 虛擬機中的工具。

圖片圖片

引導類加載器 BootstrapClassLoader

引導類加載器 BootstrapClassLoader:引導類加載器是使用 C++ 語言實現的,嵌入在 JVM 中。用于加載 Java 中的核心類庫的,不繼承自 java.lang.ClassLoader,在 Java 程序中通常返回 null。

一般會加載 JAVA_HOME 目錄下的 /jre/lib 文件夾下的 jar 和配置。

ClassLoader loader = String.class.getClassLoader();
System.out.println(loader); // 輸出 null,因為 String 是由引導類加載器加載的

擴展類加載器 ExtClassLoader

擴展類加載器主要負責加載 Java 的擴展類庫,一般會加載 JAVA_HOME 目錄下的 /jre/lib/ext 文件夾下的 jar。

繼承自 java.lang.ClassLoader,是用戶可以訪問的第一個類加載器。

ClassLoader extLoader = ClassLoader.getSystemClassLoader().getParent();
System.out.println(extLoader); // 輸出 sun.misc.Launcher$ExtClassLoader

應用類加載器(Application ClassLoader)

應用類加載器是應用程序中默認的類加載器,可以加載 CLASSPATH 變量指定目錄下的 jar,由 sun.misc.Launcher$AppClassLoader 實現。

并且一般情況下,我們編寫的 Java 應用的類,都是使用該類加載器完成加載的。

ClassLoader appLoader = ClassLoader.getSystemClassLoader();
System.out.println(appLoader); // 輸出 sun.misc.Launcher$AppClassLoader

類加載器抽象類 ClassLoader

在 Java 中存在一個類加載器抽象類 ClassLoader,大多數類加載器都是通過繼承這個類來實現的類加載功能。以下是 ClassLoader 類的關鍵部分代碼:

public abstract class ClassLoader {

    /*
     * 類加載器的父加載器
     */
    private final ClassLoader parent;

    /**
     * 根據類的全限定名加載類
     *
     * @param name 類名稱
     * @return     加載的Class對象
     * @throws ClassNotFoundException 沒有發現指定類異常
     */
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 調用loadClass方法加載類,其中設置resolve=false,表示不立即解析類
        return loadClass(name, false);
    }

    /**
     * 根據類的全限定名加載類
     *
     * @param name    類名稱
     * @param resolve 是否解析這個類,true=解析,false=不解析
     * @return 加載的Class對象
     * @throws ClassNotFoundException 沒有發現指定類異常
     */
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 檢查類是否已經被加載
            Class<?> c = findLoadedClass(name);
            // 如果沒有加載過
            if (c == null) {
                // 如果有父類加載器,則委托給父加載器去加載
                // 如果沒有父類加載器,則判斷 Bootstrap 類加載器是否加載過
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
                // 如果父類加載器都加載失敗,則當前類加載器嘗試自行加載
                if (c == null) {
                    c = findClass(name);
                }
            }
            // 據 resolve 參數決定是否解析類
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

    /**
     * 查找并加載指定名稱的類
     *
     * @param name 類名稱
     * @return Class對象
     * @throws ClassNotFoundException 沒有發現指定類異常
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //1. 根據傳入的類名,到在特定目錄下去尋找類文件,把字節碼文件讀入內存
        // ...
        //2. 調用 defineClass 將字節數組轉成 Class 對象
        return defineClass(buf, off, len);
    }

    /**
     * 將一個 byte[] 轉換為 Class 類的實例
     *
     * @param name 類名稱,如果不知道此名稱,則該參數為 null
     * @param b    組成類數據的字節數組
     * @param off  類數據的起始偏移量
     * @param len  類數據的長度
     * @return Class對象
     * @throws ClassFormatError 類格式化異常
     */
    protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError {
        ...
    }

}

類中定義的常用的類加載相關的方法:

方法名稱

描述

getParent()

返回該類加載器的父類加載器

loadClass(String name)

加載指定名稱的類,返回 java.lang.Class 實例

findClass(String name)

查找指定名稱的類,返回 java.lang.Class 實例

findLoadedClass(String name)

查找已加載的指定名稱的類,返回 java.lang.Class 實例

defineClass(String name, byte[] b, int off, int len)

將字節數組轉換為一個 Java 類,返回 java.lang.Class 實例

resolveClass(Class c)

連接指定的 Java 類

雙親委派模型(Parent Delegation Model)

雙親委派模型 是類加載器的設計模式,其核心思想是:類加載請求由子類加載器向父類加載器逐層委派,直到引導類加載器。

如果父類加載器無法加載,子類加載器才會嘗試加載。

如果子類加載器也無法加載該類,就會拋出一個 ClassNotFoundException 異常。

圖片圖片

雙親委派機制的作用

我們試想一下,如果不使用這種委托模式,那我們就可以隨時使用自定義的 String 類來動態替代 Java 核心 API 中定義的類型,這樣會存在非常大的安全隱患。

而雙親委托的方式,就可以避免這種情況,因為 String 已經在啟動時就被引導類加載器 (BootstrcpClassLoader) 加載,所以用戶自定義的 ClassLoader 永遠也無法加載一個用戶自己自定義的 String 類,除非你改變 JDK 中 ClassLoader 搜索類的默認算法。

該機制的作用如下。

  • 防止重復加載字節碼文件: 將類加載請求先委托給父類,父類加載后子類就不會重復加載該類。所以,雙親委派機制可以防止對某個類重復加載;
  • 防止核心字節碼文件被篡改: 一般情況下引導類加載器會先加載 JVM 核心類庫,然后其它加載器才會執行,如果其它加載器要加載一個被篡改的核心字節碼文件,會將該文件委托給父類加載器,當委托到引導類加載器時,加載器已經加載過該類,就不會對該類進行重復加載。而且就算能被加載,那么加載它的肯定不是相同的類加載器 (不會是引導類加載器),Java 虛擬機中只認可核心類加載器加載的核心類庫,所以,雙親委派機制可以防止核心字節碼文件被篡改。
  • 簡化加載邏輯: 通過委派模式,每個類加載器只需要關注自己負責的那部分類加載邏輯,而不必關心其他類加載器的加載細節,簡化了類加載器的實現,降低了系統的復雜度。

自定義類加載器

在某些場景下,標準的類加載器無法滿足需求,例如:

  1. 熱部署:在 Web 服務器中動態加載或更新類。
  2. 模塊隔離:在同一個 JVM 中加載不同版本的類。
  3. 加密解密:加載經過加密的 Class 文件。

默認的類加載器只能加載指定目錄下的 Jar 和 Class 文件。

如果需要加載指定位置的類文件并實現一些自定義邏輯,就需要自定義類加載器。

Chaya:如何實現自定義類加載器?

步驟:

  • 繼承 java.lang.ClassLoader 類。
  • 重寫 findClass() 方法,通過字節流讀取 Class 文件并轉換為 Class 對象。
import java.io.*;

public class MyClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] loadClassData(String name) {
        String fileName = name.replace('.', '/') + ".class";
        try (InputStream is = new FileInputStream(fileName);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int buffer;
            while ((buffer = is.read()) != -1) {
                baos.write(buffer);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

示例說明

  • findClass():從文件系統加載 Class 文件,并將其定義為 Class 對象。
  • defineClass():將字節數組轉換為 JVM 可執行的 Class 對象。

為了為保證類加載器都正確實現雙親委派機制,在開發自己的類加載器時,只需要重寫 findClass() 方法即可。

當然,如果不想使用雙親委派機制時,就需要重寫 loadClass() 方法。

打破雙親委派模型

有時為了實現特殊功能,我們需要打破雙親委派模型,例如:

  • 熱部署框架:Tomcat、Spring Boot 使用自定義類加載器加載和卸載 Web 應用。
  • SPI(Service Provider Interface)機制:JDBC 驅動等需要通過 線程上下文類加載器 來加載用戶實現的接口。
責任編輯:武曉燕 來源: 碼哥跳動
相關推薦

2024-04-09 08:41:41

JVM類加載Java

2023-12-06 12:11:43

類加載器雙親委派模型

2024-03-12 07:44:53

JVM雙親委托機制類加載器

2024-03-27 09:15:27

2020-11-06 00:50:16

JavaClassLoaderJVM

2021-07-05 06:51:43

Java機制類加載器

2023-10-31 16:00:51

類加載機制Java

2023-10-19 09:14:34

Java開發

2022-08-08 08:17:43

類隔離加載器自定義類

2020-10-26 11:20:04

jvm類加載Java

2024-12-02 09:01:23

Java虛擬機內存

2012-02-09 10:31:17

Java

2021-01-06 09:51:19

類加載器雙親委派模型

2017-03-08 10:30:43

JVMJava加載機制

2021-04-29 11:18:14

JVM加載機制

2017-09-20 08:07:32

java加載機制

2023-10-30 01:02:56

Java類類加載器雙親委派

2022-06-15 11:01:59

自定義SPIJava

2023-08-04 08:53:42

2024-03-08 08:26:25

類的加載Class文件Java
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕 亚洲一区 | 精品国产91亚洲一区二区三区www | 久久亚洲欧美日韩精品专区 | av中文字幕在线观看 | 日韩欧美一区二区三区在线播放 | 精品国产一区二区三区久久久蜜月 | 欧美久久久久久久久中文字幕 | 久久久区 | 亚洲精品久久久久久久久久久久久 | 亚洲天堂成人在线视频 | 国产三级一区二区三区 | 一级欧美 | 久久中文字幕一区 | 亚洲欧美在线观看视频 | 能看的av| 精品国产一区二区国模嫣然 | 久久久久久久久久久久久久久久久久久久 | www国产成人免费观看视频 | 99在线资源| 亚洲人成在线观看 | 久久久久国产精品一区三寸 | 国产一区二区在线免费观看 | 久久一区二区三区四区 | 中文字幕在线网 | 97免费视频在线观看 | 99爱国产 | 国产综合精品 | 国产精品成人一区二区三区 | 午夜视频免费在线观看 | 国产精品视频久久久久久 | 久久久噜噜噜www成人网 | 中文字幕人成乱码在线观看 | 亚洲啪啪 | 成人av播放 | 日本三级在线 | 一二三区视频 | av在线成人 | 91精品国产综合久久精品图片 | 久久精品视频播放 | 毛片免费观看 | 国产精品久久国产精品久久 |