一 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条)