首页 > 其他分享 >类加载器的理解——基于Launcher类

类加载器的理解——基于Launcher类

时间:2023-01-19 11:04:38浏览次数:49  
标签:基于 var1 parent Launcher null class 加载


文章目录

  • ​​一、理论入口​​
  • ​​二、创建加载器​​
  • ​​1、扩展类加载器(Ext)​​
  • ​​2、应用类加载器(App)​​
  • ​​3、补充​​
  • ​​三、双亲委派机制​​


阅读本文你将收获:

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());
}
}

类加载器的理解——基于Launcher类_System



由于引导类加载器是C++编写,所以Java获取时只能为null

根据打印结果,我们不难发现,这两个类加载器(App、Ext)是Launcher的内部类。

记住它们都实现了URLClassLoader,这对你调试代码时理清逻辑十分重要

类加载器的理解——基于Launcher类_加载器_02



二、创建加载器

加载器实现的核心类为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...

类加载器的理解——基于Launcher类_加载器_03



三、双亲委派机制

双亲委派机制这个词已经见怪不怪了,当程序加载一个类的加载器会先去它的父加载器中加载…,此处不谈概念,只有代码逻辑

以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)​​方法


标签:基于,var1,parent,Launcher,null,class,加载
From: https://blog.51cto.com/u_15942107/6019940

相关文章