文章目录

  • 一、理论入口
  • 二、创建加载器
    • 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());}
}

由于引导类加载器是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 instanceparallelLockMap = 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.1SecurityManager 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 instanceparallelLockMap = 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/classesext加载器: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 如果引导类加载器中没有找到,则直接返回nullc = 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类相关推荐

  1. 深入类加载器-类加载器作用,类缓存、类加载器的层次结构、ClassLoader类介绍、代理模式之双亲委派机制

    1.类加载器的作用 类加载器的作用是将class字节码文件加载到内存中,并将这些静态数据转化为方法区中的运行时数据结构,同时在堆中生成代表这个类的java.lang.Class对象,作为访问方法区中数 ...

  2. java 类加载器的理解及加载机制?

    通过 java 命令运行 java 程序的步骤就是指定包含 main 方法的完整类名以及一个 classpath 类路径,类路径可以有多个,对于直接的 class 文件路径就是 class 文件的根目 ...

  3. Java中的类加载器(ClassLoader)及类的加载机制

    类加载器就是用来将class文件加载到内存中的一个java类! 系统默认有三个类加载器! ①BootStrap:这不是java类,是java虚拟机在启动的时候加载的第一个类,这个加载器用来加载核心类, ...

  4. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 )

    文章目录 一.使用 DexClassLoader 获取组件类失败报错 二.失败原因分析 一.使用 DexClassLoader 获取组件类失败报错 在上一篇博客 [Android 逆向]启动 DEX ...

  5. 深入理解Java虚拟机二(类加载器和类的加载过程)

    类加载器子系统作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于它是否可以运行,则由Ex ...

  6. java类二次加载_深入理解java之类加载器

    一.类与类加载器 类加载器:实现加载阶段的第一步,通过一个类的全限定名来将这个类的二进制字节流加载进jvm. 类与类加载器:任意一个类唯一性都是由它本身和加载它的类加载器确定,两个类是否相等在它们是由 ...

  7. 类与类加载器---《深入理解java虚拟机》

    2019独角兽企业重金招聘Python工程师标准>>> 类加载器虽然只用于实现类的加载动作,但它在java程序中起到的作用却远远不限于类加载阶段. 对于任意一个类,都需要由加载它的类 ...

  8. 深入理解JVM(6)——类加载器

    虚拟机设计团队把类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流(即字节码)"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现 ...

  9. 深入理解Java类加载器:Java类加载原理解析

    http://blog.csdn.net/zhoudaxia/article/details/35824249 1 基本信息 每个开发人员对java.lang.ClassNotFoundExcetpi ...

  10. 深入理解Java类加载器(2)

    1 基本信息 每个开发人员对Java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加载机制是技术体系中比较核心的 ...

最新文章

  1. Spring、Spring Boot和TestNG测试指南 - @ActiveProfiles
  2. AM335X的汇编语言与c语言,X86汇编语言学习手记 -- 汇编和C协同
  3. 图形显卡_显卡缺货?专业图形卡主机方案演示,Quadro P2200
  4. matlab 代谢流分析,代谢网络流分析进展及应用-生物在线.PDF
  5. Go 之父:聊聊我眼中的 Go 语言和环境
  6. MongoDB学习之(二)java连接
  7. 一个关于Schema的问题,请求帮助
  8. java - 小程序二维码中间的logo占整个二维码的比例计算
  9. 蓝桥杯——练习系统登录
  10. K2P加USB口刷入Padavan
  11. 【Unity】雷达+Unity +TUIO 介绍二
  12. Overload 和 Override
  13. 富士胶片集团发布2021财年上半年度财报;罗克韦尔自动化推出新的远程访问解决方案 | 全球TMT...
  14. [Web安全]信息收集
  15. 色彩平衡校正色彩的原理
  16. Microsoft Word 实用功能
  17. c语言程序中要用到阶乘,C程序使用递归求数字的阶乘
  18. 探索推荐引擎内部的秘密系列
  19. 临床宏基因组学的应用
  20. 学习笔记(15):R语言入门基础-增加行或列

热门文章

  1. CF1039E Summer Oenothera Exhibition
  2. BZOJ3190[JLOI2013] 赛车
  3. jQuery学习笔记(边学边记版本)
  4. java 日志框架 详解_springboot日志详解
  5. java文件读取路径_java文件读取路径问与答
  6. python产生随机数组_Python生成随机数组的方法小结
  7. python中元组的赋值_在Python中,元组可以被赋值修改。( ) (2.0分)_学小易找答案...
  8. WEBPACK+ES6+REACT入门(7/7)-React中绑定文本框与state中的值
  9. 基于xml的c语言项目,Spring基于XML装配Bean
  10. 【系列二之图像处理系列】提取图片(1)