前言

学习类加载必然离开不了sun.misc.Launcher这个类和Class.forName()这个方法。

分析ClassLoader.getSystemClassLoader()这个流程可以明白下面几个知识点:

  • sun.misc.Launcher的初始化

  • 初次接触线程上下文类加载器(Thread context class loader)

  • 三个参数的Class.forName(String name, boolean initialize, ClassLoader loader)方法

  • 怎样修改JVM默认的系统类加载器

  • Launcher类中存在两个很重要的内部类:AppClassLoader和ExtClassLoader

  • Launcher类主要负责AppClassLoader的初始化/ExtClassLoader的初始化/线程上下文类加载器的初始化

  • Class.forName()是JDK提供给我们用于加载一个Class文件的方法

第一步:ClassLoader.getSystemClassLoader()

当客户端希望获取系统类加载器的时候,需要第一次调用ClassLoader.getSystemClassLoader()静态方法,该方法第一步即会去尝试获取一个sun.misc.Launcher

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

第二步:Launcher实例的初始化

这里的实例初始化是不区分方法的,这是Launcher类的静态变量的初始化,通过之前学习类加载的知识可以知道:类初始化后,类的静态变量会初始化。所以下面的代码得到调用:

    private static Launcher launcher = new Launcher();

下面我们看一下Launcher的无参构造器:

public class Launcher {
public Launcher() {
Launcher.ExtClassLoader var1;
try {
/**
* 调用静态内部类的静态方法:获取ExtClassLoader(第一次调用时会初始化)
* 这里的getExtClassLoader()方法使用了DCL模式创建一个ExtClassLoader的单例。
* 初始化的时候,内部包含ExtClassLoader的加载路径:java.ext.dirs
*/
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
/**
* 调用静态内部类的静态方法:获取AppClassLoader(第一次调用时会初始化)
* 这里的getAppClassLoader()方法没有什么特殊的,就是包含AppClassLoader的加载路径:java.class.path
*/
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//设置当前线程上下文类加载器为AppClassLoader
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 {
//SecurityManager默认是被AppClassLoader加载的
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);
}
}
}

第三步:ClassLoader.initSystemClassLoader()

说回ClassLoader.getSystemClassLoader()方法,初始化完成Launcher实例之后,下面就是初始化ClassLoader类中的SystemClassLoader了。

    private static synchronized void initSystemClassLoader() {
//如果systemClassLoader没有被设置
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
//获取Launcher
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
//获取Launcher的AppClassLoader
scl = l.getClassLoader();
try {
//使用SystemClassLoaderAction类暴露修改SystemClassLoader的功能给User
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}

第四步:利用SystemClassLoaderAction修改AppClassLoader

SystemClassLoaderAction类不是内部类,它是ClassLoader平级的类,但不是public的。

class SystemClassLoaderAction
implements PrivilegedExceptionAction<ClassLoader> {
private ClassLoader parent; SystemClassLoaderAction(ClassLoader parent) {
this.parent = parent;
} public ClassLoader run() throws Exception {
//获取系统属性java.system.class.loader
String cls = System.getProperty("java.system.class.loader");
if (cls == null) {
//系统属性java.system.class.loader为空,返回默认的AppClassLoader【这种情况为默认情况】
return parent;
}
/**
* 若系统属性java.system.class.loader(设为X)不为空,反射获取x的class(X为二进制名字)
* 然后把X对应的类加载器反射初始化,设置为系统类加载器。
* 并将X设置为线程上下文类加载器。
* 注意这里的Class.forName(String name, boolean initialize, ClassLoader loader)方法
*/
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;
}
}

第五步:三个参数的Class.forName(String name, boolean initialize, ClassLoader loader)方法

  • 参数1:类的全名
  • 参数2:true表示初始化这个class,false表示不需初始化这个class
  • 参数3:loader表示希望用哪个类加载器来加载该类
  • 返回 :代表这个名字的类的Class对象

    Class.forName(String name, boolean initialize, ClassLoader loader)方法的部分JavaDoc文档:
     * @param name       fully qualified name of the desired class
* @param initialize if {@code true} the class will be initialized.
* See Section 12.4 of <em>The Java Language Specification</em>.
* @param loader class loader from which the class must be loaded
* @return class object representing the desired class

sun.misc.Launcher中的内部类

AppClassLoader

可以看到Launcher类中存在一个静态内部类AppClassLoader,其中包含如下方法,所以这就是该问题的答案:为何应用类加载器是从java.class.path路径中加载类?

        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<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}

ExtClassLoader

Launcher类中还存在一个静态内部类ExtClassLoader,其中包含如下方法,在首次创建扩展类加载器的时候被调用,所以这就是该问题的答案:为何扩展类加载器是从java.ext.dirs路径中加载类?

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

最新文章

  1. ugui,button的一个坑
  2. MVC与DWZ整合中部分问题的解决
  3. Linux time命令
  4. path入门 20141102-1405
  5. Oracle sysdate 时间加减
  6. Java mysql 日期相关
  7. iOS开发经验总结(转)
  8. 精美的HTML5 Loadding页面
  9. HDU 4746 (莫比乌斯反演) Mophues
  10. USART笔记 基于STM32F107VCT6
  11. 谁在唱衰PC?说出你的理由
  12. 04737_C++程序设计_第5章_特殊函数和成员
  13. CSS3线性渐变linear-gradient
  14. 1.1 sikuli 安装
  15. extjs入门
  16. 从实践的角度理解cookie的几个属性
  17. 用UiPath导入RPA实践1:VirtualBox的安装
  18. 数据结构 栈&amp;队列
  19. 浅谈hibernate+入门实例
  20. JavaScript 为什么要有 Symbol 类型?

热门文章

  1. css 边框添加三角形指向,简单粗暴,易学易懂
  2. GoLang设计模式02 - 工厂模式
  3. MyBatis学习总结(六)——Mybatis3.x与Spring4.x整合
  4. Git 系列教程(1)- Git 简介
  5. TypeError: exchange_declare() got an unexpected keyword argument &#39;type&#39;
  6. Spring Cloud Eureka 之常用配置解析
  7. 图论---DFS
  8. python math详解(1)
  9. 【OI】计算分子量 Molar mass UVa 1586 题解
  10. 使用PHP获取图像文件的EXIF信息