0 ClassLoader 基本介绍
参考 本人之前写的一篇虚拟机类加载机制。
1 类加载器原理
不同ClassLoader 加载类的范围。
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("---------------BootstrapClassLoader--------------------");
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
Arrays.stream(urls).map(URL::toExternalForm).forEach(System.out::println);
System.out.println("---------------ExtClassLoader--------------------");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println("---------------AppClassLoader--------------------");
System.out.println(System.getProperty("java.class.path"));
}
Java类加载器采用双亲委派模型 ,下面我们从代码上看下究竟什么是双亲双亲委派模型?1.当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。 2.当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
3.当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
- 为什么要使用双亲委派模型?
1.安全考虑, 避免自定义类覆盖了jdk 核心类,强制加载会报java.lang.SecurityException: Prohibited package name: java.lang2.避免重复加载,比如要使用JDK类就不用每个类加载器都自己加载一遍。
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) {
long t0 = System.nanoTime();
try {
// 如果有父类加载器,先委派给父类加载器去加载
if (parent != null)
c = parent.loadClass(name, false);
} else {
如果没有尝试让BootstrapClassloader 类加载器去加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 如果都没有那自能当前classLoader 去加载
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.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
- 为什么要破坏双亲委派模型?
简单说就是隔离,防止jar 包冲突。
自定义classLoader
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> clazz = this.findLoadedClass(name);
if (clazz == null) {
byte[] classData = loadFile(name);
if (classData == null) {
throw new ClassNotFoundException();
}
clazz = defineClass(name, classData, 0, classData.length);
}
return clazz;
}
private byte[] loadFile(String name) throws ClassNotFoundException {
String resource = name.replace('.', '/').concat(".class");
ByteArrayOutputStream output = new ByteArrayOutputStream();
InputStream inputStream = null;
try {
inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resource);
byte[] buffer = new byte[1024];
int n;
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
} catch (Exception ex) {
throw new ClassNotFoundException("cannot read " + name, ex);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
}