类的加载和双亲委派模型
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的加载和双亲委派模型相关的知识,希望对你有一定的参考价值。
类加载器基本概念
顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class
类的一个实例。
任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性。
类加载器类型
- 启动类(引导类)加载器 Bootstrap ClassLoader, 虚拟机的一部分,由c++实现。负责加载<JAVA_HOME>/lib下的类库
- 扩展类加载器 Extension ClassLoader, sun.misc.Launcher$ExtClassLoader.负载加载<JAVA_HOME>/lib/ext下的类库
- 应用程序类加载器 Application ClassLoader ,sun.misc.Launcher$AppClassLoader, 它是System.getClassLoader()的返回值,也称为系统类加载器。负责加载用户类路径上所指定的类库。如果应用程序没有自定义过类加载器,一般它就是默认的类加载器。Thread.currentThread.getContextClassLoader,如果没有setContextClassLoader,默认也是它。
双亲委派模型
双亲委派模型的过程:如果一个类加载器收到了类加载的请求,首先不会自己去加载,而是把请求为派给自己的父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类反馈自己无法完成这个加载请求时,子类加载器才会尝试自己完成。
好处就是:Object类在程序的各种类加载器环境中都是同一个类。不会造成混乱。
破坏双亲委派模型
双亲委派模型只是推荐,而非强制。有三次大规模破坏该模型的情况。
- JDK 1.0->1.2 , loadClass() -> findClass()
- 模型缺陷:JNDI服务,SPI扩展类是由厂商自己实现,而启动类加载又不可能认识这些类。只好引入 线程上下文 类加载器Thread Context ClassLoader. 该类加载器可以通过setContextClassLoader()设置,如果创建线程时未设置,将会从父线程继承。如果在应用的全局范围内都没有设置,那就默认是AppClassLoader. 有了这个,JNDI服务就可以去加载所需的SPI扩展代码,也就是父类加载器请求子类加载器去完成类加载的动作。这其实也就违背了双亲委派模型的一般性原则,但无可奈何。
- 程序动态性的追求: “热替换” , OSGI. JSR-291
开发自己的类加载器
继承 java.lang.ClassLoader,覆盖findClass(String name)即可。
java.lang.ClassLoader
类的方法 loadClass()
封装了前面提到的代理模式的实现。该方法会首先调用 findLoadedClass()
方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass()
方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass()
方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()
方法,而是覆写 findClass()
方法。示例:
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace(‘.‘, File.separatorChar) + ".class";
}
}
以上是关于类的加载和双亲委派模型的主要内容,如果未能解决你的问题,请参考以下文章