类的加载器
- 引导类加载器(BootstrapClassLoader,由C++实现),负责加载支撑JVM运行的位于JRE的lib目录中核心类库,比如rt.jar等
- 扩展类加载器(ExtClassLoader),负责加载支撑JVM运行的位于JRE的lib目录下ext目录中的JAR包
- 应用程序类加载器(AppClassLoader),负责加载ClassPath下的类包,主要就是加载我们自己写的那些类
- 自定义加载器,负责加载用户自定义路径下的类包
类加载器初始化过程
- 实例化引导类加载器BootStrapClassLoader(由C++实现)
- 创建JVM启动器实例sun.misc.Launcher,该类由引导类加载器加载
- 在Launcher的构造方法中,创建了sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)实例,并指定AppClassLoader的父加载器为ExtClassLoader,同时指定AppClassLoader为默认的类加载器
private ClassLoader loader;
//Launcher的构造方法
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//构造扩展类加载器,在构造的过程中将其父加载器设置为null
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//构造应用类加载器,在构造的过程中将其父加载器设置为ExtClassLoader,
//Launcher的loader属性值是AppClassLoader,我们一般都是用这个类加载器来加载我们自己写的应用程序
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
。。。。。。
}
//获取类加载器方法
public ClassLoader getClassLoader() {
return this.loader;
}
JVM默认使用Launcher的getClassLoader方法获取的类加载器AppClassLoader加载应用程序类。
类加载双亲委派机制
JVM类加载器是有父子关系的,如下图
类加载其实就是一个双亲委派机制,先委派父级加载器加载,一直委派到引导类加载器。引导类加载器加载失败时再由拓展类加载器加载,拓展类加载器加载失败再由应用类加载器加载,以此类推。类加载器父子关系并不是继承关系,源码如下
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 判断类有没有加载过,加载过就不需要再重新加载,直接返回
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//委派父级加载器加载,深度优先一直向上委派直到parent为null,执行else由引导类加载器加载
c = parent.loadClass(name, false);
} else {
// parent为null,由引导类加载器加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//父加载器加载失败,则由自己去加载
if (c == null) {
// If still not found, then invoke findClass in order
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
如何实现一个自定义加载器?
实现自定义类加载器只需要继承 java.lang.ClassLoader 类。该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制。另一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。
package com.rs.visit.test;
import java.io.FileInputStream;
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = load(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
private byte[] load(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
public static void main(String[] args) throws Exception {
MyClassLoader classLoader = new MyClassLoader("D:/test");
Class<?> clazz = classLoader.loadClass("com.rs.visit.test.User");
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
上面代码Main()方法执行结果为:com.rs.visit.test.MyClassLoader。
如何打破双亲委派机制?
如果只是重写findClass方法,自定义类加载器依然遵循双亲委派机制。ClassLoader构造器会指定自定义类加载器父级加载器为AppClassLoader,并在执行loadClass方法遵循双亲委派机制。那么如何打破双亲委派机制,很简单上面自定义类加载器类中重写loadClass方法即可。
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}