Spring Boot本地類和Jar包類載入順序深度剖析

本文將從多個方面對Spring Boot本地類和Jar包類載入順序做詳細的闡述,並給出相應的代碼示例。

一、類載入機制概述

在介紹Spring Boot本地類和Jar包類載入順序之前,有必要簡要介紹一下Java的類載入機制。

Java虛擬機在程序運行期間動態載入類,並將類的位元組碼轉換成運行時數據結構,形成可執行代碼。類的載入過程由ClassLoader類及其子類完成。

Java虛擬機規範將ClassLoader類設計成了一個抽象類,用以描述Java虛擬機中存在的類載入器,用以載入Class文件。ClassLoader類的任務是把class文件位元組碼轉化為java.lang.Class的一個實例。而Class實例通常存放於運行時數據區的方法區內。

二、本地類和Jar包類載入順序詳解

2.1 Jar包優先於本地類

在Java類載入過程中,如果系統中存在多個相同的類文件,則優先載入JVM類路徑下的Jar包中的class文件,而忽略本地類路徑下的同名class文件。

為了說明這一點,我們可以自己手寫一個同名class文件,並同時存放在JVM類路徑下和本地類路徑下。接著,我們在代碼中進行載入並列印該class文件的內容。

// 在JVM類路徑下創建SameClass.java
package com.example.demo;

public class SameClass {
    public static void printFile() {
        System.out.println("This is a file in JVM classpath.");
    }
}
// 在本地類路徑下創建SameClass.java,內容和上述相同
package com.example.demo;

public class SameClass {
    public static void printFile() {
        System.out.println("This is a file in local classpath.");
    }
}
// 在代碼中進行載入並列印SameClass類文件的內容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoadingOrderDemoApplication.class.getClassLoader();
        Class clazz = classLoader.loadClass("com.example.demo.SameClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

運行上面的代碼之後,我們發現輸出結果是”This is a file in JVM classpath.”。

這是因為JVM類路徑下的SameClass.class文件優先被載入,而本地類路徑下的同名文件被忽略了。

2.2 本地類優先於系統類

對於Jar包中不存在的類文件,Java會首先從本地文件系統中查找,然後才會查找系統類庫(如JDK中的rt.jar)。

同樣地,我們可以手寫一個只在本地類路徑下存在的class文件,並在代碼中進行載入和列印。

// 在本地類路徑下創建OnlyLocalClass.java
package com.example.demo;

public class OnlyLocalClass {
    public static void printFile() {
        System.out.println("This is a file in local classpath, but not in the system classpath.");
    }
}
// 在代碼中進行載入並列印OnlyLocalClass類文件的內容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoadingOrderDemoApplication.class.getClassLoader();
        Class clazz = classLoader.loadClass("com.example.demo.OnlyLocalClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

運行上述代碼之後,我們可以發現列印結果為”This is a file in local classpath, but not in the system classpath.”。

由此可見,本地類優先於系統類。

2.3 父ClassLoader優先於子ClassLoader

對於同一份class文件,父ClassLoader的載入順序優先於子ClassLoader。

我們可以通過自定義ClassLoader來模擬這種情況,並在代碼中進行載入和列印。

// 自定義ClassLoader
package com.example.demo;

import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        try {
            String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream is = this.getClass().getResourceAsStream(fileName);
            if (is == null) {
                return super.loadClass(name);
            }
            byte[] b = new byte[is.available()];
            is.read(b);
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        }
    }
}
// 在本地類路徑下創建ParentClass.java
package com.example.demo;

public class ParentClass {
    public static void printFile() {
        System.out.println("This is a file in parent classloader.");
    }
}
// 在代碼中進行載入並列印ParentClass類文件的內容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader parentClassLoader = new MyClassLoader();
        ClassLoader childClassLoader = new MyClassLoader();
        
        parentClassLoader.loadClass("com.example.demo.ParentClass");
        Class clazz = childClassLoader.loadClass("com.example.demo.ParentClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

運行上述代碼之後,我們可以發現列印結果為”This is a file in parent classloader.”,即父ClassLoader先於子ClassLoader載入了該class文件。

三、小結

本文從類載入機制概述、Jar包與本地類載入順序、父ClassLoader與子ClassLoader的載入順序等多個方面,對Spring Boot本地類和Jar包類載入順序做了詳細剖析。

Java虛擬機能夠動態載入類,並將類的位元組碼轉換成運行時數據結構形成可執行代碼,類的載入過程由ClassLoader類及其子類完成。

原創文章,作者:OISGV,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/373731.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
OISGV的頭像OISGV
上一篇 2025-04-27 15:26
下一篇 2025-04-27 15:26

相關推薦

發表回復

登錄後才能評論