参考链接:https://blog.csdn.net/briblue/article/details/54973413
三大类加载器与各自加载的包,执行顺行
Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
Appclass Loader 也称为SystemAppClass 加载当前应用的classpath的所有类
加载顺序:
1. Bootstrap CLassloder
2. Extention ClassLoader
3. AppClassLoader
测试Bootstrap CLassloder 加载的东西
System.out.println(System.getProperty("sun.boot.class.path"));
结果:
C:\Program Files\Java\jre1.8.0_91\lib\resources.jar;
C:\Program Files\Java\jre1.8.0_91\lib\rt.jar;
C:\Program Files\Java\jre1.8.0_91\lib\sunrsasign.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jsse.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jce.jar;
C:\Program Files\Java\jre1.8.0_91\lib\charsets.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jfr.jar;
C:\Program Files\Java\jre1.8.0_91\classes
测试Extention ClassLoader 加载的东西
System.out.println(System.getProperty("java.ext.dirs"));
结果:
C:\Program Files\Java\jre1.8.0_91\lib\ext;C:\Windows\Sun\Java\lib\ext
测试AppClassLoader 加载的东西
System.out.println(System.getProperty("java.class.path"));
结果:
D:\workspace\ClassLoaderDemo\bin
这个路径其实就是当前java工程目录bin,里面存放的是编译生成的class文件,java工程中的bin目录一般存放的都是编译.java文件生成的二进制.class可执行文件
查看类记载器
执行代码:
public class Hello {
public static void main(String[] args) {
ClassLoader cl = Hello.class.getClassLoader();
System.out.println("ClassLoader is:"+cl.toString());
}
}
结果:
ClassLoader is:jdk.internal.loader.ClassLoaders$[AppClassLoader@4f8e5cde](mailto:AppClassLoader@4f8e5cde)
也就是说明Hello.class文件是由AppClassLoader加载的,而像int.class,string.class是由Bootstrap ClassLoader加载的
每个类加载器都有一个父加载器(父加载器不是父类)
查看AppClassLoader的父加载器,执行代码:
public static void main(String[] args) {
ClassLoader cl = Hello.class.getClassLoader();
System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());
}
结果:
ClassLoader's parent is:jdk.internal.loader.ClassLoaders$[PlatformClassLoader@311d617d](mailto:PlatformClassLoader@311d617d)
这个说明,AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的parent是null
类图比较
Bootstrap ClassLoader是由C++编写的。
Bootstrap ClassLoader是由C++编写的。
Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。然后呢,我们前面已经分析了,JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader。这也可以解释之前通过ExtClassLoader的getParent方法获取为Null的现象
双亲委托查找
一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托
用序列描述一下:
1. 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
2. 递归,重复第1部的操作。
3. 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找。
4. Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。
5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。如果没有找到就让子类找,如果没有子类会怎么样?抛出各种异常
重要方法loadClass()
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException
1. 执行findLoadedClass(String)去检测这个class是不是已经加载过了。
2. 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。
3. 如果向上委托父加载器没有加载成功,则通过findClass(String)查找,前面说过ExtClassLoader的parent为null,所以它向上委托时,系统会为它指定Bootstrap ClassLoader
要注意的是如果要编写一个classLoader的子类,也就是自定义一个classloader,建议覆盖findClass()方法,而不要直接改写loadClass()方法
例如:
if (parent != null) {
//父加载器不为空则调用父加载器的loadClass
c = parent.loadClass(name, false);
} else {
//父加载器为空则调用Bootstrap Classloader
c = findBootstrapClassOrNull(name);
}
自定义ClassLoader
不知道大家有没有发现,不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。如果在某种情况下,我们需要动态加载一些东西呢?比如从D盘某个文件夹加载一个class文件,或者从网络上下载class主内容然后再进行加载,这样可以吗?如果要这样做的话,需要我们自定义一个classloader
自定义步骤
1.编写一个类继承自ClassLoader抽象类。
2.复写它的findClass()方法。
3.在findClass()方法中调用defineClass(), 这个方法在编写自定义classloader的时候非常重要,它能将class . 二进制内容转换成Class对象,如果不符合要求的会抛出各种异常
注意:一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader
可根据链接:https://blog.csdn.net/briblue/article/details/54973413查看自定义ClassLoad的例子
关键字 路径
类加载器的关联部分就是路径,也就是要加载的class或者是资源的路径。
BootStrap ClassLoader、ExtClassLoader、AppClassLoader都是加载指定路径下的jar包。如果我们要突破这种限制,实现自己某些特殊的需求,我们就得自定义ClassLoader,自已指定加载的路径,可以是磁盘、内存、网络或者其它
常见的用法是将Class文件按照某种加密手段进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性
可根据链接:https://blog.csdn.net/briblue/article/details/54973413查看例子
每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置
总结
ClassLoader用来加载class文件的。
系统内置的ClassLoader通过双亲委托来加载指定路径下的class和资源。
可以自定义ClassLoader一般覆盖findClass()方法。
ContextClassLoader与线程相关,可以获取和设置,可以绕过双亲委托的机制