《Java 底層原理》Java 類加載器

前言

工作中需要實現一種功能:動態加載類對象信息,簡單說就是class變了Jvm能夠立馬知道並且加載到內存。

類加載器分類

Java類加載器分爲兩種,一種是加載啓動類,另一種是其他類加載器。Java加載類的關係:Launcher

1. 啓動類加載器(BootstrapClassLoader)。

Java 程序是運行在Jvm上,所以Jvm需要知道Java程序的入口在何處(啓動類)。Jvm通過c++的LoadMainClass去加載Java程序的sun.launcher.LauncherHelper。並且通過他加載Mian函數所在的擴展類ClassLOader和AppClassLoader。

2. Java自己編寫的類加載器。

2.1 ExtClassLoader 擴展類加載器

ExtClassLoader類是沒有父類的。

String var0 = System.getProperty("java.ext.dirs");

因爲上面的代碼,所以可以設置類擴展類加載器加載Jar包的目錄。

2.2 AppClassLoader Class_path 指定的Jar包

AppClassLoader的父類爲ExtClassLoader。我們main函數所在類的類加載器是AppClassLoader。

Java類加載器會加載的Jar包路徑

import sun.misc.Launcher;

import java.net.URL;

public class ClassLoaderDemo1 {
    public static void main(String[] args) {
        String[] split = System.getProperty("sun.boot.class.path").split(";");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
        System.out.println("========================================");
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urLs.length; i++) {
            System.out.println(urLs[i]);
        }
    }
}

運行結果:

委派機制

根據類加載器,畫一個Java委派和加載的原理圖。

Java不會被重新加載是通過類的全限定名來判斷的

Java委派機制的好處。

1. 保證核心包加載不受干擾。

2.  保證class 加載唯一。

SPI機制

先看個案例

public interface ServerSPI {
    void execMethod();
}

兩個實現類

public class ServerSPIImpl1 implements ServerSPI{
    @Override
    public void execMethod() {
        System.out.println("ServerSPIImpl1.execMethod 執行");
    }
}

public class ServerSPIImpl2 implements ServerSPI {
    @Override
    public void execMethod() {
        System.out.println("ServerSPIImpl2.execMethod 執行");
    }
}

配置(目錄要一致,文件名是接口名)

文件內容(類的全限定名)

寫一個測試類

import sun.misc.Service;
import java.util.Iterator;

public class SpiTest {
    public static void main(String[] args) {
        Iterator<ServerSPI> providers = Service.providers(ServerSPI.class);

        while(providers.hasNext()) {
            ServerSPI ser = providers.next();
            ser.execMethod();
        }
    }
}

運行結果:

ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);   -- 這個方式也可以獲取加載的對象。

該案例沒有使用Java的委派機制實現了類的加載功能。

Service 的源碼重點:

private static final String prefix = "META-INF/services/";    -- 爲什麼目錄是指定的。
    public static <S> Iterator<S> providers(Class<S> var0) throws ServiceConfigurationError {
        ClassLoader var1 = Thread.currentThread().getContextClassLoader();     -- 獲取線程所在個類ClassLoader。
        return providers(var0, var1);
    }

SPI加載類的原理:

1. 需要指定目錄,程序需要在知道目錄下找到接口信息,根據接口找到實現類。

2. 通過線程所在的ClassLoader 加載實現,通過反射實例化對象。

3. 其他細節源碼,有興趣自己瞭解。

4. 我們經常使用的JDBC就是使用SPI機制實現類加載。

自定義類加載器

實現熱部署功能案例:https://www.cnblogs.com/jssj/p/13251804.html

案例中很好的運用了自定義類加載器,並且實現瞭如何打破Java委派機制。

總結

類加載器的種類,類加載器的各自分工,類加載的委派過程,SPI機制,自定義加載器的實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章