ClassLoader
URLClassLoader
AppClassLoader
ExtClassLoader
1、将ExtClassLoader作为父加载器传给AppClassLoder,而ExtClassLoader的父加载器传的是null
2、ExtClassLoader加载的目录是:java.ext.dirs,AppClassLoader加载的目录是java.class.path
3、这里再说一下AppClassLoder,ExtClassLoader extentds URLClassLoader,父类和父加载器是两个概念,父加载器是组合模式,持有父加载器的引用
-----------------------------------------
加载流程:
从前一节main方法的执行可以看到,加载一个普通类说起,是用系统类加载器,即AppClassLoader
-> AppClassLoader.loadClass->ClassLoader.loadClass -> parent.loadClass
-> ExtClassLoader.loadClass->ClassLoader.loadClass -> findBootstrapClassOrNull -> null -> findClass
-> URLClassLoader.findClass -> Resource res = ucp.getResource(path, false); //在java.ext.dirs目录下加载不到用户的类 -> res == null -> throw ClassNotFoundException
-> AppClassLoader.loadClass catch ClassNotFoundException -> null -> findClass
-> URLClassLoader.findClass -> res != null
-> ClassLoader.defineClass -> native ClassLoader.defineClass1 -> load success
所以最终是流程是:
1、AppClassLoader.loadClass:调用父类ClassLoader.loadClass方法
2、parent.loadClass:递归调用父加载器 ExtClassLoader.loadClass -> BootStrapClassLoader.loadClass,加载用户类失败
3、ExtClassLoader findClass:URLClassLoader.findClass: java.ext.dirs目录下加载class文件失败 -> throw ClassNotFound,在AppClassLoader Catch
4、AppClassLoader findClass:URLClassLoader.findClass:java.class.path目录下加载class文件成功,并加载二进制流
5、ClassLoader defineClass:URLClassLoader.findClass中再调用ClassLoader.defineClass,传入二进制流,完成Class组装
总结:
1、通过上述流程可以看到,其实ClassLoader已经干了大部分的活,ClassLoader实现了委派机制,
2、子类只需要实现findClass,findClass方法中:1需要加载class文件(二进制流),2调用ClassLoader.defineClass传入二进制流
3、AppClassLoader、ExtClassLoader 都是继承自URLClassLoader.findClass,只是在初始化的时候将加载目录的urls传给了URLClassLoader,URLClassLoader.findClass通过urls加载class文件
自定义加载器:
从上面的总结2可以看出,自定义加载器只需要实现findClass完成class文件的加载,然后在调用ClassLoader.defineClass传入二进制流即可再来看看官方推荐: The network class loader subclass must define the methods {@link #findClassfindClass} andloadClassDatato load a class from the network. Once it has downloaded the bytes that make up the class, it should use the method {@link #defineClassdefineClass} to create a class instance. A sample implementation is:
class NetworkClassLoader extends ClassLoader { String host; int port; public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection }}
以上就是个标准的自定义加载器了:
1、定义defineClass,加载class文件二进制流 2、调用父类ClassLoader.defineClas,传入二进制流,完成类的加载
使用:
NetworkClassLoader loader = new NetworkClassLoader();
loader.loadClass("com....");
注意:new NetworkClassLoader()并未传入父加载器,但是会调用ClassLoader的无参构造方法通过getSystemClassLoader传入系统类加载器,即AppClassLoader
所以,自定义加载器若没有特殊指定父加载器,则默认父加载器为AppClassLoader
当父加载器:BootStrapClassLoader,ExtClassLoader,AppClassLoader都加载不到时,触发自身的findClass方法