自定义类加载器及其双亲委托机制
- JAVA自带的类加载器
- 自定义类加载器
-
- 双亲委托机制
JAVA自带的类加载器
JAVA的类加载器分为三种,启动类加载器(系统类加载器)、扩展类加载器以及系统类加载器
由前到后每一个都是下一个的父加载器
获取类加载器以及加载器的加载路径
System.out.println("----------------三种类加载器-------------------");
System.out.println(Launcher.getLauncher().getClassLoader());
// 启动类加载器输出为空,别慌不是你错了,这就是正确结果
System.out.println(Launcher.getLauncher().getClassLoader().getParent());
System.out.println(Launcher.getLauncher().getClassLoader().getParent().getParent());
System.out.println("---------------------各个类加载的路径---------------------------");
System.out.println(System.getProperty("sun.boot.class.path"));
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
输出结果
----------------三种类加载器-------------------
sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$ExtClassLoader@74a14482
null
---------------------各个类加载的路径---------------------------
C:\Java\jdk1.8.0_65\jre\lib\resources.jar;C:\Java\jdk1.8.0_65\jre\lib\rt.jar;C:\Java\jdk1.8.0_65\jre\lib\sunrsasign.jar;C:\Java\jdk1.8.0_65\jre\lib\jsse.jar;C:\Java\jdk1.8.0_65\jre\lib\jce.jar;C:\Java\jdk1.8.0_65\jre\lib\charsets.jar;C:\Java\jdk1.8.0_65\jre\lib\jfr.jar;C:\Java\jdk1.8.0_65\jre\classes
C:\Java\jdk1.8.0_65\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
C:\Java\jdk1.8.0_65\jre\lib\charsets.jar;C:\Java\jdk1.8.0_65\jre\lib\deploy.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_65\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_65\jre\lib\javaws.jar;C:\Java\jdk1.8.0_65\jre\lib\jce.jar;C:\Java\jdk1.8.0_65\jre\lib\jfr.jar;C:\Java\jdk1.8.0_65\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_65\jre\lib\jsse.jar;C:\Java\jdk1.8.0_65\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_65\jre\lib\plugin.jar;C:\Java\jdk1.8.0_65\jre\lib\resources.jar;C:\Java\jdk1.8.0_65\jre\lib\rt.jar;E:\亿达信息\GoodGoodStadyDayDayUp\out\production\GoodGoodStadyDayDayUp;E:\亿达信息\GoodGoodStadyDayDayUp\lib\mysql-connector-java-5.0.8-bin.jar;E:\idea\IntelliJ IDEA 2017.2\lib\idea_rt.jar
在这些路径中(每个人的也许会不同,所以按照自己的来修改),可以尝试把自己写的Class放入到指定的文件中(ExtClassLoader只加载Jar包中的类),去通过getClassLoader()方法获取已验证结果,这里不做过多介绍。
如果你很好奇我怎么知道获取路径的是这些参数,那就请参考一下sun.misc.Launcher源代码。。。
自定义类加载器
代码示例
package com.programer.wenbin.jvm;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader {
private String path;
private String classLoaderName;
private final String fileExtension = ".class";
public MyClassLoader(String classLoaderName) {
super();// 使用系统类加载器作为父加载器
this.classLoaderName = classLoaderName;
}
public MyClassLoader(ClassLoader parent, String classLoaderName) {
super(parent);// 使用传入的类加载器作为父加载器
this.classLoaderName = classLoaderName;
}
/**
* 根据文件的地址,以字节的形式加载class文件。
*
* @param {String}name
* @return 加载class的byte数组
*/
private byte[] loadClassData(String name) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
try {
name = name.replace(".", "\\");
is = new FileInputStream(new File(this.path + name + this.fileExtension));
baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("----------------------This is MyClassLoader------------------");
byte[] data = this.loadClassData(name);
return super.defineClass(name, data, 0, data.length);
}
public void setPath(String path) {
this.path = path;
}
}
![](https://img-blog.csdnimg.cn/20200530142453525.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjA2MDc3OQ==,size_16,color_FFFFFF,t_70)
打开ClassLoader类会看到注释中解释了怎么建立了一个简单的自定义加载器
测试加载器
MyClassLoader loader = new MyClassLoader("loader1");
loader.setPath("C:\\Users\\13559\\Desktop\\jvmtest\\");
// com.programer.wenbin.jvm.TestUse这个类是自己建立的一个普通java类,可以没有任何一个属性和方法
Class<?> clazz = loader.loadClass("com.programer.wenbin.jvm.TestUse");
System.out.println(clazz.getClassLoader());
setPath是这只一个加载的路径我们把需要加载的类放入进去即可,如下图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200530143428466.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjA2MDc3OQ==,size_16,color_FFFFFF,t_70)
测试已经准备好,准备你们的小手点击运行,那么问题来了!
sun.misc.Launcher$AppClassLoader@14dad5dc
这输出的怎么是AppClassLoader(系统类加载器),这也就是今天要说的重点双亲委托机制
双亲委托机制
- 概念
当使用一个类加载器去加载一个class对象的时候,这个加载器会用自己的父类加载器去尝试加载,如果加载不成功才会自己去加载
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200530150926463.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjA2MDc3OQ==,size_16,color_FFFFFF,t_70)
会发现递归调用,这也就是双亲委托的核心代码 - 啃老的好处
这里有一个命名空间的概念,我以后可能会专门发一个关于这个的,如果没有(懒了),请大家去CSDN中进行搜索。这里简要的说一下:
命名空间是由加载器以及所有的父类加载器所组成的,自加载器可以访问父类加载的类,父类加载器不能访问子加载器加载的类,儿子可以用爸爸的,爸爸不可以用儿子的,兄弟之间的也是不可以用的,由此可以得出结论加载器的一家过的很不和谐
…有点说多了,简单的说一下好处吧,这样就能保证JAVA的核心类,比如最牛*的Object类,是由启动类加载器加载的,这样使的Object只加载一次,因为如果已经加载过的类,并且是可见的,那么就不会再一次加载,“开箱即用”。如果对于这个类不可见的话,就会再一次加载Obejct,所以存在很多版本的Object类,使得程序存在潜在风险(JDK设计人员的脑子牛!),但是这种也不是万能的,比如JDBC等,所以有的时候会违背双亲委托机制,会使用线程上下文类加载器,以后别的文章会说。
如果想用自己的类加载器加载,怎么办呢?
上面开始提到过AppClassLoader的加载路径,那么我们就把已经编译好的文件删除掉去运行,如果是ecplise就打开文件夹进行删除,idea在目录里进行删除即可
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200530150502935.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjA2MDc3OQ==,size_16,color_FFFFFF,t_70)
删除之后请大家再次运行
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200530151114795.png)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)