众所周知,类加载主要是通过类加载器实现的,但是大多数人都是知其然,而不知其所以然,HelloWorld这个类大家都不陌生,从这个类开始聊一聊类加载器和双亲委派机制;
package com.fanqiechaodan.classloader;
/**
* @date 2021/6/24 21:44
* @declare
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
- 我们的类开始运行的时候,首先会先通过jvm.ddl文件创建jvm,就是我们的Java虚拟机;
- 创建一个引导类加载器bootstrapLoader,前两步都是通过C++实现的;
- 通过引导类加载器实例化一个Launcher类;负责加载创建其它类加载器
- 通过launcher.getClassLoader()创建AppClassLoader应用程序加载器,这个加载器是负责运行我们自己写的那些类
- appClassLoader.loadClass(“com.fanqiechaodan.classloader.HelloWorld”);加载我们要运行的类
- 执行main();此时HelloWorld就可以输出再我们的控制台了
Java里有以下几种类加载器:
- 引导类加载器(bootstrapLoader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar(Launcher就在tr这个jar里面)、resources.jar等
- 扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包
- 应用程序类加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那 些类(idea中target文件夹下的类)
- 自定义加载器:特殊情况下,需要使用我们自己写的类加载器;负责加载我们自定义路径下的类
package com.fanqiechaodan.classloader;
import sun.net.spi.nameservice.dns.DNSNameService;
/**
* @date 2021/6/24 22:33
* @declare
*/
public class MyClassLoader {
public static void main(String[] args) {
//获取Character的类加载器,Character是rt.jar下面的类
System.out.println(Character.class.getClassLoader());
//获取DNSNameService的类加载器,DNSNameService是dnsns.jar下面的类;dnsns.jar是ext扩展目录的jar
System.out.println(DNSNameService.class.getClassLoader());
//获取MyClassLoader的类加载器;MyClassLoader是我们自己写的类,属于target文件夹下面的
System.out.println(MyClassLoader.class.getClassLoader());
System.out.println();
//获取应用程序加载器
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
//获取应用程序加载器的父加载器:扩展类加载器
ClassLoader extClassLoader = appClassLoader.getParent();
//获取扩展类加载器:引导类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(appClassLoader);
System.out.println(extClassLoader);
System.out.println(bootstrapClassLoader);
}
}
//输出结果:
null
sun.misc.Launcher$ExtClassLoader@3cbbc1e0
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@3cbbc1e0
null
从代码的输出结果上来看,三个类加载器是存在子父关系的,至于为什么bootstrapClassLoader为什么会输出一个null,这个我们再上面已经讲过了,引导类加载器是C++负责实例化的,我们再java代码中是看不到的,所以输出的是一个null值;并且还可以看出ExtClassLoader和AppClassLoader都是Launcher类下的内部类;我们从Launcher的源码中看一下,是不是这么回事.
public class Launcher {
private static URLStreamHandlerFactory factory = new Launcher.Factory();
//再类初始化时,就实例Launcher,方便再getLauncher()中返回,
//单例设计模式
private static Launcher launcher = new Launcher();
private static String bootClassPath = System.getProperty("sun.boot.class.path");
private ClassLoader loader;
private static URLStreamHandler fileHandler;
/*
* 获取Launcher实例;
*/
public static Launcher getLauncher() {
//直接返回launcher
return launcher;
}
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//首先获取一个ExtClassLoader扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//然后获取一个AppClassLoader,并且把刚刚获取到的ExtClassLoader当作参数传进去
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
//一些安全校验...
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
//....省略部分代码
//Launcher.ExtClassLoader代码
static class ExtClassLoader extends URLClassLoader {
/*
* 获取ExtClassLoader实例;
*/
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
final File[] var0 = getExtDirs();
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
MetaIndex.registerDirectory(var0[var2]);
}
//再方法最后会直接实例化一个ExtClassLoader
return new Launcher.ExtClassLoader(var0);
}
});
} catch (PrivilegedActionException var2) {
throw (IOException)var2.getException();
}
}
void addExtURL(URL var1) {
super.addURL(var1);
}
/*
* ExtClassLoader构造方法;
*/
public ExtClassLoader(File[] var1) throws IOException {
//实例化子类先调用父类的构造方法;parent传值是null
//bootstrapClassLoader是C++实现的,java代码传null
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
//....省略部分代码
//Launcher.AppClassLoader代码
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
/*
* 获取AppClassLoader实例;
* @Param var0:就是ExtClassLoader
*/
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
//java.class.path,就是我们自己写的类路径
//再idea显示就是target文件夹
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
//再最后会实例化一个AppClassLoader,将ExtClassLoader当作参数
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
/**
* AppClassLoader的构造方法
* @param var1 负载加载的路径地址数组
* @Param var2 ExtClassLoader
*/
AppClassLoader(URL[] var1, ClassLoader var2) {
//实例化子类先调用父类的构造方法;parent传值是ExtClassLoader
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
//URLClassLoader的构造方法
//AppClassLoader和ExtClassLoader都继承了URLClassLoader
//再实例化的时候都会调用URLClassLoader的构造方法
//ExtClassLoader调用时,parent的值传的是null或者说是bootstrapClassLoader
//AppClassLoader调用时,parent的值传的是ExtClassLoader
//子父级关系明显
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
acc = AccessController.getContext();
ucp = new URLClassPath(urls, factory, acc);
}
- Launcher.getLauncher(),返回的是再类初始化时就实例化的Launcher;属于单例模式
- Launcher的构造方法,会先调用Launcher.ExtClassLoader.getExtClassLoader()获取一个ExtClassLoader
- var1=Launcher.ExtClassLoader.getExtClassLoader()最后会直接实例化一个ExtClassLoader;ExtClassLoader继承URLClassLoader这个类;实例化子类会先调用父类的构造方法: super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);parent的值传的是null或者说是bootstrapClassLoader
- 接着会调用Launcher.AppClassLoader.getAppClassLoader(var1);获取一个AppClassLoader的实例;调用getAppClassLoader()时,会把ExtClassLoader当作参数传进去,同样的到最后也会实例化一个AppClassLoader,AppClassLoader也继承URLClassLoader这个类;实例化子类会先调用父类的构造方法: super(var1, var2, Launcher.factory);var1是AppClassLoader负责加载的类路径数组;var2就是parent这个值传的是ExtClassLoader.子父级关系明显
- 注意:这里面说的子父级关系不是继承关系,不是AppClassLoader继承ExtClassLoader,他们两个都继承URLClassLoader,子父关系说的是AppClassLoader的parent属性是ExtClassLoader;
最后说一下类加载的机制,双亲委派机制:
URLClassLoader继承ClassLoader这个类,我们的类加载主要是通过loadClass(String name)方法,接下来就直接看一下ClassLoader.loadClass(String name)方法的源码
/**
* 方法的重载
* 我们调用的都是loadClass(String name)这个方法
* 这个方法内部默认传入false调用它的重载方法
*
* @param name
* @return
* @throws ClassNotFoundException
* If the class could not be found
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* 重载方法
*
* @param name 类的全限定类名
* @param resolve 是否解析,如果为true的话会解析这个类,默认传false,不再这里进行解析
* @return
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 首先,检查当前类加载器是否已经加载了该类
Class<?> c = findLoadedClass(name);
//对结果进行判空
if (c == null) {
//为空的情况
long t0 = System.nanoTime();
try {
//对当前加载器的父加载器判空
if (parent != null) {
//当前加载器的父加载器不为空则委托父加载器加载该类
c = parent.loadClass(name, false);
} else {
//如果当前加载器父加载器为空则委托引导类加载器加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//对结果再次进行判空,判断父加载器是否加载到该类
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//到最后都会调用URLClassLoader的findClass()
//在加载器的类路径里查找并加载该类
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//默认false,永远不会执行
resolveClass(c);
}
return c;
}
}
- 首先,检查一下当前的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
- 如果此类没有加载过,再判断一下父加载器是否为空(是否有父类加载器);如果不为空,则调用parent.loadClass(name, false)由父加载器加载,如果父加载器为空调用findBootstrapClassOrNull(name)由bootstrapClassLoader类加载器来加载.
- 如果父加载器及bootstrap类加载器都没有找到当前,那么调用当前类加载器的 findClass方法来完成类加载,最后将结果返回
- 双亲委派机制顺序:
4.1 AppClassLoader
4.2 ExtClassLoader
4.3 bootstrapClassLoader
4.4 ExtClassLoader
4.5 AppClassLoader - 总结:简单点说就是儿子先不加载,先找父亲加载,父亲不负责加载,再由儿子进行加载
为什么要有双亲委派机制?
避免类的重复加载,当儿子准备加载时,先让父亲去加载,父亲如果加载过了就直接返回,没有必要再让儿子再加载一遍;