
一 what is Classloader二 内置Classloader
2.1 Bootstrap Classloader2.2 ExtClassloader2.3 AppClassloader2.4 小结 三 自定义`ClassLoader`
3.1 单亲委派3.2 如何绕过单亲委派3.3
本篇系统介绍class loader 及其应用.
笔者以为,在讨论计算机(编程) 中的概念时首先一定要确定 概念所在的维度(范畴)是什么,否则会发现,大家说的貌似都对, 但哪里总有点不对.
当我们说 Java Classloader时,我们要搞清楚是说 Classloader 这个类/对象呢, 还是 Classloader机制.
二 内置Classloader这个图是网上泛滥的, 面试说烂了的 Classloader 继承机制,学名叫做 双亲委派. 这个翻译很囧, 英文其实是parent delegating , 本意是 单亲委派…
这里留个坑,后面我们继续讲 怎么delegating.
Bootstrap Classloader是Classloader体系中的顶层加载器. “顶层"的意思就是说” 我是祖先".
Classloader体系中的 Bootstrap Classloader 和 Object类作为所有的类的顶级父类, 是有着异曲同工之妙的.
Bootstrap Classloader是使用C++写的, 负责JVM最核心类库的加载,比如 java.lang 包, e.g., String.class. 可以通过-xbootclasspath指定Bootstrap Classloader的路径,也能通过系统属性得知当前Bootstrap Classloader加载了哪些资源.
有意思的是 ,Bootstrap Classloader 是没法从JVM中拿到引用的.
public static void main(String[] args) {
// BootstrapClassloader=>null 根类加载器是获取不到引用的
System.out.println("BootstrapCL=>" + String.class.getClassLoader());
// E:Javajdk1.8.0_121jrelibresources.jar;
// E:Javajdk1.8.0_121jrelibrt.jar;
// E:Javajdk1.8.0_121jrelibsunrsasign.jar;
// E:Javajdk1.8.0_121jrelibjsse.jar;
// E:Javajdk1.8.0_121jrelibjce.jar;
// E:Javajdk1.8.0_121jrelibcharsets.jar;
// E:Javajdk1.8.0_121jrelibjfr.jar;
// E:Javajdk1.8.0_121jreclasses
System.out.println(System.getProperty("sun.boot.class.path"));
// C:WINDOWSSunJavalibext
System.out.println(System.getProperty("java.ext.dirs"));
// sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(CL.class.getClassLoader());
}
2.2 ExtClassloader
ExtClassloader用来加载 JAVA_HOME下的 jrelibext库,类似的, 可以通过 java.ext.dirs获取路径.
我们可以做一个小测试,写一个最简单的Hello world, 打成jar,然后将该jar 放到
JAVA_HOME下的 jrelibext下, 再跑下列的main ,会发现 是ExtClassLoader; 如果 再把这个jar删除了, 又是AppClassLoader.
public static void main(String[] args) throws Exception{
// sun.misc.Launcher$ExtClassLoader@5f5a92bb
ClassLoader loader = Class.forName("com.code.hello.Hello").getClassLoader();
System.out.println(loader);
// sun.misc.Launcher$AppClassLoader@18b4aac2
}
2.3 AppClassloader
AppClassloader,也叫 SystemClassloader (系统classloader), 负责加载类路径下的类库资源,说白了就是你的应用的类路径. AppClassloader是自定义Classloader的父Classloader.
那怎么获取类路径呢? System.getProperty("java.class.path")
2.4 小结前面说的三个大佬是内置ClassLoader, 实际在很多中间件中有自定义的ClassLoader,比如tomcat的ClassLoader就是个经典案例.
三 自定义ClassLoaderClassLoader 是一个抽象类, 要实现自定义的ClassLoader ,只要 继承并override findClass方法即可,比如下面的代码.
这个MyCL类就是自定义的ClassLoader, 它从磁盘读取一个class 文件, 加载进JVM, 甚至还new 出了一个对象!
public class MyCL extends ClassLoader{
// class文件的全路径名
private String clazzFilePath;
public MyCL(ClassLoader parent, String classFile) {
super(parent);
this.clazzFilePath = classFile;
}
public MyCL(String classFile) {
this.clazzFilePath = classFile;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] clazzBytes;
try {
clazzBytes = Files.readAllBytes(Paths.get(clazzFilePath));
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException("IO exception",e);
}
return defineClass(name, clazzBytes, 0, clazzBytes.length);
}
public static void main(String[] args) throws Exception{
MyCL myCL = new MyCL("G:\github\code-snipptes\hello-world\target\classes\com\code\hello\Hello.class");
Class> helloClazz = myCL.findClass("com.code.hello.Hello");
Object instance = helloClazz.newInstance();
// com.code.hello.Hello@c39f790
System.out.println(instance);
//com.code.cl.MyCL@1ff8b8f
System.out.println(instance.getClass().getClassLoader());
}
}
3.1 单亲委派
为啥叫单亲委派,看看源码即知.
我们并没有看到 "双亲委派"中的"双亲"体现在哪里. 这种机制本质就是: 总是让父加载器优先去加载.
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// findLoadedClass()本质是个 native方法,用来记录当前这个加载器是不是已经加载过这个类
Class> c = findLoadedClass(name);
// 这个类并没有加载过
if (c == null) {
long t0 = System.nanoTime();
try {
// 若父加载器存在, 则委派给 父加载器 会加载
// 这里一定要理解下, parent.loadClass()同样会执行一次 loadClass
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 否则委派给 根加载器
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
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// ingore....
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
3.2 如何绕过单亲委派
假如有个HelloWorld.class ,默认由 AppClassLoader加载. 假如我想绕过去,即使用我MyCL去加载呢?
前面
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)