类加载器的理解——基于Launcher类
Posted 默辨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载器的理解——基于Launcher类相关的知识,希望对你有一定的参考价值。
阅读本文你将收获:
1、应用类加载器的父加载器为什么是扩展类加载器。
2、扩展类加载器的父加载器为什么是null。
3、双亲委派机制的向上委派过程
一、理论入口
类加载器分类:
- 引导类加载器:加载lib目录下的核心类库,如rt.jar、charsets.jar等
- 扩展类加载器:加载lib目录下ext扩展目录的jira包
- 应用类加载器:自己编写的类的加载器
- 自定义类加载器:自己实现ClassLoader类,并重写对应的findClass方法
测试代码:
public class Mobian {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(SunEC.class.getClassLoader());
System.out.println(Mobian.class.getClassLoader());
System.out.println("=======");
System.out.println(Mobian.class.getClassLoader());
System.out.println(Mobian.class.getClassLoader().getParent());
System.out.println(Mobian.class.getClassLoader().getParent().getParent());
}
}
由于引导类加载器是C++编写,所以Java获取时只能为null
根据打印结果,我们不难发现,这两个类加载器(App、Ext)是Launcher的内部类。
记住它们都实现了URLClassLoader,这对你调试代码时理清逻辑十分重要
二、创建加载器
加载器实现的核心类为Launcher类,接下来展示该类中实例化加载器的具体顺序
记住一定要是sun.misc包路径下的Launcher类
// 默认的无参构造方法
public Launcher() {
Launcher.ExtClassLoader var1;
try {
// 一、获取扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
// 二、根据扩展类加载器获取应用加载器
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
...
}
1、扩展类加载器(Ext)
static class ExtClassLoader extends URLClassLoader {
private static volatile Launcher.ExtClassLoader instance;
// 这里是一个经典的双重检测锁单例模式
// 1、前面获取扩展类加载器的入口
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
// 2、由于使用单例模式,则可以确定扩展类加载器只会存在一个
instance = createExtClassLoader();
}
}
}
return instance;
}
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
// 3、获取扩展类加载器的目录
// 其本质就是处理改目录下的jar包,System.getProperty("java.ext.dirs");
File[] var1 = Launcher.ExtClassLoader.getExtDirs();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
MetaIndex.registerDirectory(var1[var3]);
}
// 4、通过扩展类加载器的目录转化的数组,创建我们的扩展类加载器
return new Launcher.ExtClassLoader(var1);
}
});
} catch (PrivilegedActionException var1) {
throw (IOException)var1.getException();
}
}
...
}
// 4.1 第二个参数为null,该参数则表示扩展类加载器的父加载器为null
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
// Creates a new class loader using the specified parent class loader for delegation.
// 1)上面的super方法最终会调用到这里,
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
// this指向下面的方法
// 2)完成该加载器的父加载器赋值,并完成其他附属操作,parent属性为null
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
assertionLock = this;
}
}
2、应用类加载器(App)
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
// 1、创建应用加载器方法调用入口
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
// 2、获取系统配置的参数变量,同上面的System.getProperty("java.ext.dirs")操作
final String var1 = System.getProperty("java.class.path");
// 3、将获取的字符串转化为File的数组
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);
// 4、返回一个应用类加载器
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
AppClassLoader(URL[] var1, ClassLoader var2) {
//5、调用对应的super方法,完成创建
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
...
}
// 5.1 调用父类的构造方法
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
// 调用ClassLoader的构造方法
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);
}
// 最终最终,它会和上面创建扩展类加载器走到同一段逻辑
// 1)不同的时,此时的parent是扩展类加载器(创建扩展类加载器的时候,parent参数为null)
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
// parent参数完成赋值,即表示应用类加载器的父加载器是扩展类加载器
private ClassLoader(Void unused, ClassLoader parent) {
// 2)完成父加载器属性的赋值
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
assertionLock = this;
}
}
3、补充
public class TestSysProperty {
public static void main(String[] args) {
// 引导类加载器时扫描的jar包位置
System.out.println("boot加载器:");
URL[] urls = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
// 扩展类加载器扫描的jar包
System.out.println("ext加载器:"+System.getProperty("java.ext.dirs"));
// 应用类加载器扫描的jar包
System.out.println("app加载器"+System.getProperty("java.class.path"));
}
}
boot加载器:
file:/E:/Environment/java/jdk8/jre/lib/resources.jar
file:/E:/Environment/java/jdk8/jre/lib/rt.jar
file:/E:/Environment/java/jdk8/jre/lib/sunrsasign.jar
file:/E:/Environment/java/jdk8/jre/lib/jsse.jar
file:/E:/Environment/java/jdk8/jre/lib/jce.jar
file:/E:/Environment/java/jdk8/jre/lib/charsets.jar
file:/E:/Environment/java/jdk8/jre/lib/jfr.jar
file:/E:/Environment/java/jdk8/jre/classes
ext加载器:E:\\Environment\\java\\jdk8\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext
// 尽管这里获取了很多jar,但是只有在我们项目的target目录下的才有用
app加载器E:\\Environment\\java\\jdk8\\jre\\lib\\charsets.jar;E:\\Environment\\java\\jdk8\\jre\\lib...
三、双亲委派机制
双亲委派机制这个词已经见怪不怪了,当程序加载一个类的加载器会先去它的父加载器中加载…,此处不谈概念,只有代码逻辑
以AppClassLoader类(Launcher类的内部类)的loadClass方法为起点
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
...
if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
// 调用父类的loadClass方法,获取类加载器
return super.loadClass(var1, var2);
}
}
// AppClassLoader继承了URLClassLoader,所以调用到父类的loadClass方法
// URLClassLoader又调用了它的父类的父类的loadClass方法
// SecureClassLoader继承了ClassLoader类,但是没有重写loadClass方法
super.loadClass(var1, var2);
// 最终它调用的是ClassLoader类的这个方法loadClass
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 {
// 父加载器为null,就表示是引导类加载器
// 不为null,就找该加载器的父加载器
if (parent != null) {
// 此时的调用parent的loadClass方法表示
c = parent.loadClass(name, false);
} else {
// parent == null,则表示该类加载器是扩展类加载器,则类去引导类加载器中加载
// 底层调用native Class<?> findBootstrapClass 如果引导类加载器中没有找到,则直接返回null
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
...
}
...
return c;
}
}
上面这个方法重点为c = parent.loadClass(name, false)
方法。回顾我们上面创建加载器时的parent参数,它表示我们的加载器,紧接上面的示例,AppClassLoader的parent参数表示它的父加载器,即ExtClassLoader,那么这就回到了我们该节示例最开始的位置(我们是从AppClassLoader的loadClass方法开始的),刚好一圈。下一圈就是走ExtClassLoader的loadClass方法,然后一层一层,此时ExtClassLoader的parent参数为null,那么它就会调用c = findBootstrapClassOrNull(name)
方法
以上是关于类加载器的理解——基于Launcher类的主要内容,如果未能解决你的问题,请参考以下文章