博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第五章 类加载器ClassLoader源码解析
阅读量:6449 次
发布时间:2019-06-23

本文共 4567 字,大约阅读时间需要 15 分钟。

说明:了解ClassLoader前,先了解 

 

1、ClassLoader作用

  • 类加载流程的"加载"阶段是由类加载器完成的。

 

2、类加载器结构

结构:BootstrapClassLoader(祖父)-->ExtClassLoader(爷爷)-->AppClassLoader(也称为SystemClassLoader)(爸爸)-->自定义类加载器(儿子

关系:看括号中的排位;彼此相邻的两个为父子关系,前为父,后为子

2.1、BootstrapClassLoader

  • 下边简称为boot
  • C++编写
  • 为ExtClassLoader的父类,但是通过ExtClassLoader的getParent()获取到的是null(在类加载器部分:null就是指boot)
  • 主要加载:E:\Java\jdk1.6\jre\lib\*.jar(最重要的就是:rt.jar)

2.2、ExtClassLoader:

  • 下边简称为ext
  • java编写,位于sun.misc包下,该包在你导入源代码的时候是没有的,需要重新去下
  • 主要加载:E:\Java\jdk1.6\jre\lib\ext\*.jar(eg.dnsns.jar)

2.3、AppClassLoader:

  • 下边简称为app
  • java编写,位于sun.misc包下
  • 主要加载:类路径下的jar

2.4、自定义类加载器:

  • 下边简称为custom
  • 自己编写的类加载器,需要继承ClassLoader类或URLClassLoader,并至少重写其中的findClass(String name)方法,若想打破双亲委托机制,需要重写loadClass方法
  • 主要加载:自己指定路径的class文件

 

3、全盘负责机制

概念:假设ClassLoaderA要加载class B,但是B引用了class C,那么ClassLoaderA先要加载C,再加载B,"全盘"的意思就是,加载B的类加载器A,也会加载B所引用的类

 

4、双亲委托机制

这也是类加载器加载一个类的整个过程

过程:假设我现在从类路径下加载一个类A,

1)那么app会先查找是否加载过A,若有,直接返回;

2)若没有,去ext检查是否加载过A,若有,直接返回;

3)若没有,去boot检查是否加载过A,若有,直接返回;

4)若没有,那就boot加载,若在E:\Java\jdk1.6\jre\lib\*.jar下找到了指定名称的类,则加载,结束;

5)若没找到,boot加载失败;

6)ext开始加载,若在E:\Java\jdk1.6\jre\lib\ext\*.jar下找到了指定名称的类,则加载,结束;

7)若没找到,ext加载失败;

8)app加载,若在类路径下找到了指定名称的类,则加载,结束;

9)若没有找到,抛出异常ClassNotFoundException

注意:

  • 在上述过程中的1)2)3)4)6)8)后边,都要去判断是否需要进行"解析"过程 ("解析"见 )
  • 类的加载过程只有向上的双亲委托,没有向下的查询和加载,假设是ext在E:\Java\jdk1.6\jre\lib\ext\*.jar下加载一个类,那么整个查询与加载的过程与app无关。
  • 假设A加载成功了,那么该类就会缓存在当前的类加载器实例对象C中,key是(A,C)(其中A是类的全类名,C是加载A的类加载器对象实例),value是对应的java.lang.Class对象
  • 上述的1)2)3)都是从相应的类加载器实例对象的缓存中进行查找
  • 进行缓存的目的是为了同一个类不被加载两次
  • 使用(A,C)做key是为了隔离类,假设现在有一个类加载器B也加载了A,key为(A,B),则这两个A是不同的A。这种情况怎么发生呢?
    • 假设有custom1、custom2两个自定义类加载器,他们是兄弟关系,同时加载A,这就是有可能的了

总结

  • 从底向上检查是否加载过指定名称的类从顶向下加载该类。(在其中任何一个步骤成功之后,都会中止类加载过程)
  • 双亲委托的好处:假设自己编写了一个java.lang.Object类,编译后置于类路径下,此时在系统中就有两个Object类,一个是rt.jar的,一个是类路径下的,在类加载的过程中,当要按照全类名去加载Object类时,根据双亲委托,boot会加载rt.jar下的Object类,这是方法结束,即类路径下的Object类就没有加载了。这样保证了系统中类不混乱。

 

5、源代码

1     /** 2      * 根据指定的binary name加载class。 3      * 步驟: 4      * 假设我现在从类路径下加载一个类A, 5      * 1)那么app会先查找是否加载过A(findLoadedClass(name)),若有,直接返回; 6      * 2)若没有,去ext检查是否加载过A(parent.loadClass(name, false)),若有,直接返回; 7      * findBootstrapClassOrNull(name) 3)4)5)都是这个方法 8      * 3)若没有,去boot检查是否加载过A,若有,直接返回; 9      * 4)若没有,那就boot加载,若在E:\Java\jdk1.6\jre\lib\*.jar下找到了指定名称的类,则加载,结束;10      * 5)若没找到,boot加载失败;11      * findClass(name) 6)7)8)9)都是这个方法12      * 在findClass中调用了defineClass方法,该方法会生成当前类的java.lang.Class对象13      * 6)ext开始加载,若在E:\Java\jdk1.6\jre\lib\ext\*.jar下找到了指定名称的类,则加载,结束;14      * 7)若没找到,ext加载失败;15      * 8)app加载,若在类路径下找到了指定名称的类,则加载,结束;16      * 9)若没有找到,抛出异常ClassNotFoundException17      * 注意:在上述过程中的1)2)3)4)6)8)后边,都要去判断是否需要进行"解析"过程18      */19     protected synchronized Class
loadClass(String name, boolean resolve)20 throws ClassNotFoundException {21 Class c = findLoadedClass(name);//检查要加载的类是不是已经被加载了22 if (c == null) {
//没有被加载过23 try {24 if (parent != null) {25 //如果父加载器不是boot,递归调用loadClass(name, false)26 c = parent.loadClass(name, false);27 } else {
//父加载器是boot28 /*29 * 返回一个由boot加载过的类;3)30 * 若没有,就去试着在E:\Java\jdk1.6\jre\lib\*.jar下查找 4)31 * 若在bootstrap class loader的查找范围内没有查找到该类,则返回null 5)32 */33 c = findBootstrapClassOrNull(name);34 }35 } catch (ClassNotFoundException e) {36 //父类加载器无法完成加载请求37 }38 if (c == null) {39 //如果父类加载器未找到,再调用本身(这个本身包括ext和app)的findClass(name)来查找类40 c = findClass(name);41 }42 }43 if (resolve) {44 resolveClass(c);45 }46 return c;47 }
View Code

说明:

  • 该段代码中引用的大部分方法实质上都是native方法
  • 其中findClass方法的类定义如下:
    /**     * 查找指定binary name的类     * 该类应该被ClassLoader的实现类重写     */    protected Class
    findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
    View Code
  • 关于findClass可以查看URLClassLoader.findClass(final String name),其中引用了defineClass方法,在该方法中将二进制字节流转换为了java.lang.Class对象

 

附:关于递归

递归基于栈实现。

上述的代码如果不清楚递归的意义是看不清的。

解释:

  • app_loadClass()方法执行到ext_loadClass(),这时候对于app_loadClass()中剩余的findClass()会在栈中向下压;
  • 然后执行ext_loadClass(),当执行到findBootstrapClassOrNull(name),这时候ext_loadClass()中剩余的findClass()也会从栈顶向下压,此时ext_loadClass()_findClass()仅仅位于app_loadClass()_findClass()的上方;
  • 然后执行findBootstrapClassOrNull(name),当boot检测过后并且执行完加载后并且没成功,boot方法离开栈顶;
  • 然后执行此时栈顶的ext_loadClass()_findClass()
  • 然后执行此时栈顶的app_loadClass()_findClass()

这样,就完成了双亲委托机制。

递归太烦了,实际开发中尽量不要用!

转载地址:http://iwlwo.baihongyu.com/

你可能感兴趣的文章
java概念基础笔记整理
查看>>
self parent $this关键字分析--PHP
查看>>
CC_UNUSED_PARAM 宏含义的解释
查看>>
leetcode124二叉树最大路径和
查看>>
AngularJS笔记整理 内置指令与自定义指令
查看>>
学习OpenCV——BOW特征提取函数(特征点篇)
查看>>
shell与正则表达式
查看>>
第三篇:白话tornado源码之请求来了
查看>>
10分钟搞定支付宝和微信支付的各种填坑
查看>>
表示数值的字符串
查看>>
JQUERY AJAX请求
查看>>
html css 伪样式
查看>>
超级账本Fabric区块链用弹珠游戏Marbles 部署
查看>>
Maven多模块项目
查看>>
Oracle、PostgreSQL与Mysql数据写入性能对比
查看>>
整理Java基础知识--选择与判断
查看>>
Linux查看程序端口占用情况
查看>>
jar包冲突案例分析.md
查看>>
控制圈复杂度的9种重构技术总结
查看>>
当软件项目全部能靠自己搞定了,也能接几万元的软件项目时,未必适合创业...
查看>>