参考《深入理解java虚拟机》
双亲委派模式
从java虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器是由c++语言实现的,是虚拟机自身的一部分;另一种就是所有其它的类加载器,这些类加载器都是由java语言实现,独立于虚拟机外部,并且全都继承了抽象类java.lang.ClassLoader。
绝大部分程序都会使用到以下三种类加载器:
启动类加载器(
Bootstrap ClassLoader
):这个类加载器负责将存放在<JAVA_HOME>/lib目录下的类库加载到虚拟机内存中,如:rt.jar;
扩展类加载器(Extension ClassLoader):这个类加载器负责加载<JAVA_HOME>/lib/ext目录下 *.jar;
应用类加载器(Application ClassLoader):这个类加载器负责加载用户类路径(classPath)上所指定的类库。
这些加载器之间的父子关系一般不会以继承来实现,而是使用组合关系复用父加载器的代码;这种层级关系被称为
双亲委派机制
双亲委派模型的工作过程是:如果一个类加载器收到类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的
启动类加载中,只用当父加载器反馈之间无法完成这个加载请求时,子类才会去尝试加载;
使用双金委派模型的好处是:java类随着它的加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object,它存放在rt.jar之中,无论哪一个加载器都要加载这个类,最终都是委派给处于最顶端的启动类加载器,因此Object类在程序的各个类加载器环境中都是同一个类。相反如果没有双亲委派模型,由各个加载器去自行加载的话,如果用户之间编写了一个java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也就无法保障,应用程序也将会一片混乱;
双亲委派模型实现起来很简单,都集中在java.lang.ClassPath的loadClass()方法中,代码如下:
protected Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
synchronized(this.getClassLoadingLock(var1)) {
// 首先检查请求的类是否已经被加载过
Class var4 = this.findLoadedClass(var1);
if(var4 == null) {
long var5 = System.nanoTime();
try {
if(this.parent != null) {
var4 = this.parent.loadClass(var1, false);
} else {
var4 = this.findBootstrapClassOrNull(var1);
}
} catch (ClassNotFoundException var10) {
;
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if(var4 == null) {
long var7 = System.nanoTime();
// 在父类加载器无法加载的时候,再调用本身的findClass方法进行类加载
var4 = this.findClass(var1);
PerfCounter.getParentDelegationTime().addTime(var7 - var5);
PerfCounter.getFindClassTime().addElapsedTimeFrom(var7);
PerfCounter.getFindClasses().increment();
}
}
if(var2) {
this.resolveClass(var4);
}
return var4;
}
}
下面是自己写的 自定义类加载器
public class MyClassLoader extends ClassLoader {
private String path; // 加载器路径
private String name; // 类加载器名称
public MyClassLoader(String name, String path){
super(); // 应用加载器成为该类的父类加载器
this.path = path;
this.name = name;
}
public MyClassLoader(ClassLoader parent, String name, String path){
super(parent); //调用父类加载器的构造方法
this.path = path;
this.name = name;
}
/**
* 通过自定义ClassLoader加载自定义类
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = readClassFileToByteArray(name);
return this.defineClass(name, data, 0, data.length);
}
/**
* 获取.class字节码数组
* D:\test\com\jvm\demo.java
* @param name
* @return
*/
private byte[] readClassFileToByteArray(String name) {
InputStream iStream = null;
byte[] returnData = null;
name = name.replaceAll("\\.", "/");
String filePath = this.path + name + ".class";
File file = new File(filePath);
ByteArrayOutputStream oStream = new ByteArrayOutputStream();
try {
iStream = new FileInputStream(file);
int tmp = 0;
while ((tmp = iStream.read()) != -1) { // 表示还存在
oStream.write(tmp);
}
returnData = oStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
iStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return returnData;
}
}
测试
package jvm;
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
MyClassLoader loader = new MyClassLoader("test", "D:/tmp/");
Class<?> findClass = loader.loadClass("aaa");
findClass.newInstance();
}
}
自定义加载器分析的比较透彻的链接:JVM——自定义类加载器