java start launcher_【深入理解Java虚拟机】Launcher 源码分析

Launcher 源码分析

拓展类加载器是Launcher 类的一个内部类,为了学习应用类加载器和拓展类加载器的实现细节,我们需要对Launcher 类的源码进行学习。

由于 Launcher 的代码属于 Oracle 闭源的代码部分,这一块代码的分析属于 IDEA 反编译出来的,所以部分变量的命名为var1, var2 这种奇怪的形式

1、获取系统类加载器

想要获取系统类加载器,可以通过 ClassLoader.getSystemClassLoader() 获取到, 这个方法中实现了对系统类加载的创建,他的说明文档中也阐述了部分细节。

/**

* Returns the system class loader for delegation. This is the default

* delegation parent for new ClassLoader instances, and is

* typically the class loader used to start the application.

*

*

This method is first invoked early in the runtime's startup

* sequence, at which point it creates the system class loader and sets it

* as the context class loader of the invoking Thread.

*

*

The default system class loader is an implementation-dependent

* instance of this class.

*

*

If the system property "java.system.class.loader" is defined

* when this method is first invoked then the value of that property is

* taken to be the name of a class that will be returned as the system

* class loader. The class is loaded using the default system class loader

* and must define a public constructor that takes a single parameter of

* type ClassLoader which is used as the delegation parent. An

* instance is then created using this constructor with the default system

* class loader as the parameter. The resulting class loader is defined

* to be the system class loader.

大致翻译一下:

此方法返回系统类加载器,这个加载器是使用 ClassLoader 创建的加载器的默认父加载器,它通常被用来启动应用程序。在程序早期运行的时候,此方法将会被第一次执行。当此方法被执行的时候,程序会创建系统类加载器 & 并将其设置其调用该方法的上下文类加载器。这个系统类加载器是依赖于实现的一个类的实例,(即AppClassloader是抽象类ClassLoader类的一个实现,系统类加载器是这个实现类的一个实例)。

如果系统的环境变量 java.system.class.loader 在此方法第一次执行之前被设置的话,这个属性的值代表的ClassLoader将会被作为指定的系统类加载器,这个被指定的系统类加载器要求提供一个公有的接收一个ClassLoader参数的构造方法,这个CLassLoader会被作为此类的父类加载器。这个类的加载器实例会被默认的系统类加载器创建,返回的系统类加载器就是系统类加载。

2、加载器的实现细节

public static ClassLoader getSystemClassLoader() {

// 尝试初始化系统类加载器,我们的重点在这个地方

initSystemClassLoader();

// 省略代码 ....

return scl;

}

继续深入 initSystemClassLoader() 方法,这是一个静态的线程安全的方法,其方法定义为

private static synchronized void initSystemClassLoader() {

// ...

sun.misc.Launcher l = sun.misc.Launcher.getLauncher();

// scl: System ClassLoader 的简称

scl = l.getClassLoader();

}

可以观察到Launcher 的创建通过 sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); 创建,这里面我们就来看看 getLauncher() 的内部是如何实现,下面的代码示例是Launcher 的静态获取方法,以及其构造方法。在其构造方法中创建了拓展类加载器以及系统类加载器。

public class Launcher {

private static Launcher launcher = new Launcher();

private ClassLoader loader;

public static Launcher getLauncher() {

return launcher;

}

public Launcher() {

// 创建拓展类加载器通过 getExtClassLoader() 方法

Launcher.ExtClassLoader var1;

try {

var1 = Launcher.ExtClassLoader.getExtClassLoader();

} catch (IOException var10) {

throw new InternalError("Could not create extension class loader", var10);

}

// 创建应用类加载器通过 getAppClassLoader(ClassLoader var1)

// 这里的var1 指的是拓展类加载,将会被作为系统类加载器的父类加载器

try {

this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);

} catch (IOException var9) {

throw new InternalError("Could not create application class loader", var9);

}

Thread.currentThread().setContextClassLoader(this.loader);

scl = AccessController.doPrivileged(new SystemClassLoaderAction(scl));

}

// 以下代码忽略

}

可以看到Launcher 这个类的构造方法中初始化了拓展类加载器和应用类加载器,在创建应用类加载器的时候,将拓展类加载器作为参数传到应用类加载器的方法中, 即 Launcher.AppClassLoader.getAppClassLoader(var1); 下面我们来分别看看这两个加载器的实现过程。

在 Thread.currentThread().setContextClassLoader(this.loader); 这行代码中也印证了注释中阐述的 at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.

2.1、拓展类加载器的创建

Launcher 方法中通过 Launcher.ExtClassLoader.getExtClassLoader(); 放创建拓展类加载器的实例,其核心代码如下:

// 这里使用单例设计模式的DCL的方法,保证单例

public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {

if (instance == null) {

Class var0 = Launcher.ExtClassLoader.class;

synchronized(Launcher.ExtClassLoader.class) {

if (instance == null) {

instance = createExtClassLoader();

}

}

}

return instance;

}

// 创建ExtClassloader

private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {

try {

return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction() {

public Launcher.ExtClassLoader run() throws IOException {

// 获取需要加载的类的文件目录信息

File[] var1 = Launcher.ExtClassLoader.getExtDirs();

int var2 = var1.length;

// 分别注册拓展类需要加载的目录

for(int var3 = 0; var3 < var2; ++var3) {

MetaIndex.registerDirectory(var1[var3]);

}

// 返回拓展类加载器

return new Launcher.ExtClassLoader(var1);

}

});

} catch (PrivilegedActionException var1) {

throw (IOException)var1.getException();

}

}

// 获取拓展类加载器的需要加载的目录

private static File[] getExtDirs() {

String var0 = System.getProperty("java.ext.dirs");

File[] var1;

if (var0 != null) {

StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);

int var3 = var2.countTokens();

var1 = new File[var3];

for(int var4 = 0; var4 < var3; ++var4) {

var1[var4] = new File(var2.nextToken());

}

} else {

var1 = new File[0];

}

return var1;

}

通过上面的核心代码的注释可以明确的看到 拓展类加载器读取了 java.ext.dirs 的环境变量的值,所以我们这里可以通过在 java的启动参数添加环境变量就可以修改环境变量加载的目录,比如 java -Djava.ext.dirs=/tmp/classes 加载并注册之后通过new的方式返回了拓展类加载器

2.2、系统类加载器的创建

系统类加载器和拓展类加载器加载方式类似,有点明显的区别是,创建系统类加载器的时候,是将拓展类加载器作为参数传入,Launcher.AppClassLoader.getAppClassLoader(extClassload);  而在构造应用类加载器 的时候,将拓展类加载作为构造参数传入我们这里可以大胆的猜测,这里试讲拓展类加载器作为应用类加载的而设置,系统类加载器的构造方法如下:

AppClassLoader(URL[] var1, ClassLoader var2) {

super(var1, var2, Launcher.factory);

this.ucp.initLookupCache(this);

}

// 其super() 方法定义为

public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {

super(parent);

// ........

}

创建系统类加载器和创建拓展类加载的过程类似,这里读者可以自行的浏览源码

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {

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() {

public Launcher.AppClassLoader run() {

URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);

return new Launcher.AppClassLoader(var1x, var0);

}

});

}

2.3、SystemClassLoaderAction

在拓展类加载器和系统类加载器加载完成,并且将会系统类加载器设置当前线程的上下文类加载器之后,源码中还执行了如下代码

if (l != null) {

Throwable oops = null;

scl = l.getClassLoader();

try {

scl = AccessController.doPrivileged(

new SystemClassLoaderAction(scl));

} catch (PrivilegedActionException pae) {

oops = pae.getCause();

if (oops instanceof InvocationTargetException) {

oops = oops.getCause();

}

}

核心是:scl = AccessController.doPrivileged(new SystemClassLoaderAction(scl)); SystemClassLoaderAction 并不是一个类加载器,记得在开头的注释中,我们说 如果系统的环境变量 java.system.class.loader 在此方法第一次执行之前被设置的话,这个属性的值代表的ClassLoader将会被作为指定的系统类加载器,这个被指定的系统类加载器要求提供一个公有的接收一个ClassLoader参数的构造方法,这个CLassLoader会被作为此类的父类加载器。 这个 SystemClassLoaderAction 主要就是判断是否配置了 java.system.class.loader 属性,如创建了通过类的反射创建此加载器的实例,将上面创建的系统类加载器作为其父类加载器构建,并返回作为新的类加载器.

明白这个之后,下面的 SystemClassLoaderAction 代码块就非常简单了

class SystemClassLoaderAction implements PrivilegedExceptionAction {

private ClassLoader parent;

SystemClassLoaderAction(ClassLoader parent) {

this.parent = parent;

}

public ClassLoader run() throws Exception {

String cls = System.getProperty("java.system.class.loader");

if (cls == null) {

return parent;

}

// 通过反射获取存在CLassLoader作为唯一参数的构造器,这也是注释中为什么要求 说

// java.system.class.loader 指向的类加载器需要有一个公有的且只有一个CLassLoader作为形参的构造器的原因

Constructor> ctor = Class.forName(cls, true, parent).getDeclaredConstructor(new Class>[] { ClassLoader.class });

ClassLoader sys = (ClassLoader) ctor.newInstance(

new Object[] { parent });

Thread.currentThread().setContextClassLoader(sys);

return sys;

}

3、汇总

通过 Launcher的方法,我们熟悉了拓展类加载器以及系统类加载器的实现细节,以及拓展类加载器作为系统类加载器父类的实现方法

在两个类加载的实现内部,也可以明确的看到 java.ext.dirs 以及 java.class.path 的作用,在必要的时候,可以通过设置参数来达到自己加载其他地方Class文件的目的

Reflection.getCallerClass(); 可以获取到调用当前方法的 Class 信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值