《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机制,自定义加载器的实现。