自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JVM 類(lèi)加載器有哪些?雙親委派機(jī)制的作用是什么?如何自定義類(lèi)加載器?

開(kāi)發(fā) 前端
引導(dǎo)類(lèi)加載器 BootstrapClassLoader:引導(dǎo)類(lèi)加載器是使用 C++ 語(yǔ)言實(shí)現(xiàn)的,嵌入在 JVM 中。用于加載 Java 中的核心類(lèi)庫(kù)的,不繼承自 java.lang.ClassLoader,在 Java 程序中通常返回 null。

類(lèi)加載器分類(lèi)

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

而 類(lèi)加載器(Class Loader)則是加載階段中,負(fù)責(zé)將本地或網(wǎng)絡(luò)中的指定類(lèi)的二進(jìn)制流,加載到 Java 虛擬機(jī)中的工具。

圖片圖片

引導(dǎo)類(lèi)加載器 BootstrapClassLoader

引導(dǎo)類(lèi)加載器 BootstrapClassLoader:引導(dǎo)類(lèi)加載器是使用 C++ 語(yǔ)言實(shí)現(xiàn)的,嵌入在 JVM 中。用于加載 Java 中的核心類(lèi)庫(kù)的,不繼承自 java.lang.ClassLoader,在 Java 程序中通常返回 null。

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

ClassLoader loader = String.class.getClassLoader();
System.out.println(loader); // 輸出 null,因?yàn)?String 是由引導(dǎo)類(lèi)加載器加載的

擴(kuò)展類(lèi)加載器 ExtClassLoader

擴(kuò)展類(lèi)加載器主要負(fù)責(zé)加載 Java 的擴(kuò)展類(lèi)庫(kù),一般會(huì)加載 JAVA_HOME 目錄下的 /jre/lib/ext 文件夾下的 jar。

繼承自 java.lang.ClassLoader,是用戶(hù)可以訪問(wèn)的第一個(gè)類(lèi)加載器。

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

應(yīng)用類(lèi)加載器(Application ClassLoader)

應(yīng)用類(lèi)加載器是應(yīng)用程序中默認(rèn)的類(lèi)加載器,可以加載 CLASSPATH 變量指定目錄下的 jar,由 sun.misc.Launcher$AppClassLoader 實(shí)現(xiàn)。

并且一般情況下,我們編寫(xiě)的 Java 應(yīng)用的類(lèi),都是使用該類(lèi)加載器完成加載的。

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

類(lèi)加載器抽象類(lèi) ClassLoader

在 Java 中存在一個(gè)類(lèi)加載器抽象類(lèi) ClassLoader,大多數(shù)類(lèi)加載器都是通過(guò)繼承這個(gè)類(lèi)來(lái)實(shí)現(xiàn)的類(lèi)加載功能。以下是 ClassLoader 類(lèi)的關(guān)鍵部分代碼:

public abstract class ClassLoader {

    /*
     * 類(lèi)加載器的父加載器
     */
    private final ClassLoader parent;

    /**
     * 根據(jù)類(lèi)的全限定名加載類(lèi)
     *
     * @param name 類(lèi)名稱(chēng)
     * @return     加載的Class對(duì)象
     * @throws ClassNotFoundException 沒(méi)有發(fā)現(xiàn)指定類(lèi)異常
     */
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 調(diào)用loadClass方法加載類(lèi),其中設(shè)置resolve=false,表示不立即解析類(lèi)
        return loadClass(name, false);
    }

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

    /**
     * 查找并加載指定名稱(chēng)的類(lèi)
     *
     * @param name 類(lèi)名稱(chēng)
     * @return Class對(duì)象
     * @throws ClassNotFoundException 沒(méi)有發(fā)現(xiàn)指定類(lèi)異常
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //1. 根據(jù)傳入的類(lèi)名,到在特定目錄下去尋找類(lèi)文件,把字節(jié)碼文件讀入內(nèi)存
        // ...
        //2. 調(diào)用 defineClass 將字節(jié)數(shù)組轉(zhuǎn)成 Class 對(duì)象
        return defineClass(buf, off, len);
    }

    /**
     * 將一個(gè) byte[] 轉(zhuǎn)換為 Class 類(lèi)的實(shí)例
     *
     * @param name 類(lèi)名稱(chēng),如果不知道此名稱(chēng),則該參數(shù)為 null
     * @param b    組成類(lèi)數(shù)據(jù)的字節(jié)數(shù)組
     * @param off  類(lèi)數(shù)據(jù)的起始偏移量
     * @param len  類(lèi)數(shù)據(jù)的長(zhǎng)度
     * @return Class對(duì)象
     * @throws ClassFormatError 類(lèi)格式化異常
     */
    protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError {
        ...
    }

}

類(lèi)中定義的常用的類(lèi)加載相關(guān)的方法:

方法名稱(chēng)

描述

getParent()

返回該類(lèi)加載器的父類(lèi)加載器

loadClass(String name)

加載指定名稱(chēng)的類(lèi),返回 java.lang.Class 實(shí)例

findClass(String name)

查找指定名稱(chēng)的類(lèi),返回 java.lang.Class 實(shí)例

findLoadedClass(String name)

查找已加載的指定名稱(chēng)的類(lèi),返回 java.lang.Class 實(shí)例

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

將字節(jié)數(shù)組轉(zhuǎn)換為一個(gè) Java 類(lèi),返回 java.lang.Class 實(shí)例

resolveClass(Class c)

連接指定的 Java 類(lèi)

雙親委派模型(Parent Delegation Model)

雙親委派模型 是類(lèi)加載器的設(shè)計(jì)模式,其核心思想是:類(lèi)加載請(qǐng)求由子類(lèi)加載器向父類(lèi)加載器逐層委派,直到引導(dǎo)類(lèi)加載器。

如果父類(lèi)加載器無(wú)法加載,子類(lèi)加載器才會(huì)嘗試加載。

如果子類(lèi)加載器也無(wú)法加載該類(lèi),就會(huì)拋出一個(gè) ClassNotFoundException 異常。

圖片圖片

雙親委派機(jī)制的作用

我們?cè)囅胍幌?,如果不使用這種委托模式,那我們就可以隨時(shí)使用自定義的 String 類(lèi)來(lái)動(dòng)態(tài)替代 Java 核心 API 中定義的類(lèi)型,這樣會(huì)存在非常大的安全隱患。

而雙親委托的方式,就可以避免這種情況,因?yàn)?String 已經(jīng)在啟動(dòng)時(shí)就被引導(dǎo)類(lèi)加載器 (BootstrcpClassLoader) 加載,所以用戶(hù)自定義的 ClassLoader 永遠(yuǎn)也無(wú)法加載一個(gè)用戶(hù)自己自定義的 String 類(lèi),除非你改變 JDK 中 ClassLoader 搜索類(lèi)的默認(rèn)算法。

該機(jī)制的作用如下。

  • 防止重復(fù)加載字節(jié)碼文件: 將類(lèi)加載請(qǐng)求先委托給父類(lèi),父類(lèi)加載后子類(lèi)就不會(huì)重復(fù)加載該類(lèi)。所以,雙親委派機(jī)制可以防止對(duì)某個(gè)類(lèi)重復(fù)加載;
  • 防止核心字節(jié)碼文件被篡改: 一般情況下引導(dǎo)類(lèi)加載器會(huì)先加載 JVM 核心類(lèi)庫(kù),然后其它加載器才會(huì)執(zhí)行,如果其它加載器要加載一個(gè)被篡改的核心字節(jié)碼文件,會(huì)將該文件委托給父類(lèi)加載器,當(dāng)委托到引導(dǎo)類(lèi)加載器時(shí),加載器已經(jīng)加載過(guò)該類(lèi),就不會(huì)對(duì)該類(lèi)進(jìn)行重復(fù)加載。而且就算能被加載,那么加載它的肯定不是相同的類(lèi)加載器 (不會(huì)是引導(dǎo)類(lèi)加載器),Java 虛擬機(jī)中只認(rèn)可核心類(lèi)加載器加載的核心類(lèi)庫(kù),所以,雙親委派機(jī)制可以防止核心字節(jié)碼文件被篡改。
  • 簡(jiǎn)化加載邏輯: 通過(guò)委派模式,每個(gè)類(lèi)加載器只需要關(guān)注自己負(fù)責(zé)的那部分類(lèi)加載邏輯,而不必關(guān)心其他類(lèi)加載器的加載細(xì)節(jié),簡(jiǎn)化了類(lèi)加載器的實(shí)現(xiàn),降低了系統(tǒng)的復(fù)雜度。

自定義類(lèi)加載器

在某些場(chǎng)景下,標(biāo)準(zhǔn)的類(lèi)加載器無(wú)法滿(mǎn)足需求,例如:

  1. 熱部署:在 Web 服務(wù)器中動(dòng)態(tài)加載或更新類(lèi)。
  2. 模塊隔離:在同一個(gè) JVM 中加載不同版本的類(lèi)。
  3. 加密解密:加載經(jīng)過(guò)加密的 Class 文件。

默認(rèn)的類(lèi)加載器只能加載指定目錄下的 Jar 和 Class 文件。

如果需要加載指定位置的類(lèi)文件并實(shí)現(xiàn)一些自定義邏輯,就需要自定義類(lèi)加載器。

Chaya:如何實(shí)現(xiàn)自定義類(lèi)加載器?

步驟:

  • 繼承 java.lang.ClassLoader 類(lèi)。
  • 重寫(xiě) findClass() 方法,通過(guò)字節(jié)流讀取 Class 文件并轉(zhuǎn)換為 Class 對(duì)象。
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;
        }
    }
}

示例說(shuō)明

  • findClass():從文件系統(tǒng)加載 Class 文件,并將其定義為 Class 對(duì)象。
  • defineClass():將字節(jié)數(shù)組轉(zhuǎn)換為 JVM 可執(zhí)行的 Class 對(duì)象。

為了為保證類(lèi)加載器都正確實(shí)現(xiàn)雙親委派機(jī)制,在開(kāi)發(fā)自己的類(lèi)加載器時(shí),只需要重寫(xiě) findClass() 方法即可。

當(dāng)然,如果不想使用雙親委派機(jī)制時(shí),就需要重寫(xiě) loadClass() 方法。

打破雙親委派模型

有時(shí)為了實(shí)現(xiàn)特殊功能,我們需要打破雙親委派模型,例如:

  • 熱部署框架:Tomcat、Spring Boot 使用自定義類(lèi)加載器加載和卸載 Web 應(yīng)用。
  • SPI(Service Provider Interface)機(jī)制:JDBC 驅(qū)動(dòng)等需要通過(guò) 線程上下文類(lèi)加載器 來(lái)加載用戶(hù)實(shí)現(xiàn)的接口。
責(zé)任編輯:武曉燕 來(lái)源: 碼哥跳動(dòng)
相關(guān)推薦

2024-04-09 08:41:41

JVM類(lèi)加載Java

2024-03-12 07:44:53

JVM雙親委托機(jī)制類(lèi)加載器

2023-12-06 12:11:43

類(lèi)加載器雙親委派模型

2024-03-27 09:15:27

2020-11-06 00:50:16

JavaClassLoaderJVM

2023-10-19 09:14:34

Java開(kāi)發(fā)

2021-07-05 06:51:43

Java機(jī)制類(lèi)加載器

2023-10-31 16:00:51

類(lèi)加載機(jī)制Java

2022-08-08 08:17:43

類(lèi)隔離加載器自定義類(lèi)

2024-12-02 09:01:23

Java虛擬機(jī)內(nèi)存

2020-10-26 11:20:04

jvm類(lèi)加載Java

2012-02-09 10:31:17

Java

2021-01-06 09:51:19

類(lèi)類(lèi)加載器雙親委派模型

2017-09-20 08:07:32

java加載機(jī)制

2021-04-29 11:18:14

JVM加載機(jī)制

2017-03-08 10:30:43

JVMJava加載機(jī)制

2023-10-30 01:02:56

Java類(lèi)類(lèi)加載器雙親委派

2023-08-02 08:38:27

JVM加載機(jī)制

2022-06-15 11:01:59

自定義SPIJava

2021-06-16 00:57:16

JVM加載機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)