前言
工作中需要實現一種功能:動態加載類對象信息,簡單說就是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機制,自定義加載器的實現。