自定义类加载器的父类为何是AppClassLoader说明
作者:小小少年_
首先有一个概念要了解
我们通常说在jdk中,默认有三个类加载器,bootstrapClassLoader、ExtClassLoader、AppClassLoader,我们说前者是后者的父类加载器,但是实际上,这里所谓的父类加载器,并不是Java中的父子类继承关系,而是说:
- AppClassLoader中有一个parentClassLoader设置的值是ExtClassLoader
- ExtClassLoader中的parentClassLoader设置的是bootstrapClassLoader
- 我们自定义的类加载器,对应的parentClassLoader是AppClassLoader
我们如果自定义一个类加载器,默认设置的父类加载器是AppClassLoader,这个原因是在因为在初始化自定义类加载器的时候,会指定其parentClassLoader为AppClassLoader
Launcher
这个类是jre中用来启动main()方法的入口,在这个类中,我们着重关注的是初始化构造方法
public Launcher() { Launcher.ExtClassLoader var1; try { // 初始化extClassLoader var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { // 初始化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"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } }
这个构造方法中,我目前所了解到的,就是初始化了AppClassLoader和ExtClassLoader,并且,和这篇博客相关的,我们只需要关心this.loader这个全局变量,这个全局变量存放的是AppClassLoader的对象信息
构造一个自定义类加载器
我们要自定义一个类加载器,只需要继承classLoader,并重写findClass()方法即可
class MyClassLoader extends ClassLoader { private String loadPath; public String getLoadPath() { return loadPath; } public void setLoadPath(String loadPath) { this.loadPath = loadPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadByte(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { System.out.println(e.getMessage()); } return null; } private byte[] loadByte(String className) throws Exception { className = className.replaceAll("\\.", "/"); FileInputStream fileInputStream = new FileInputStream(loadPath + "/" + className + ".class"); int available = fileInputStream.available(); byte[] data = new byte[available]; fileInputStream.read(data); fileInputStream.close(); return data; } }
那么,我在使用的时候,只需要调用new MyClassLoaderTest()即可
new MyClassLoaderTest()
我们说,自定义的类解析器对应的parentClassLoader,就是在空参构造函数中被赋值的
因为MyClassLoaderTest继承了ClassLoader,所以,会调用到ClassLoader的空参构造函数
protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader()); }
private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { parallelLockMap = new ConcurrentHashMap<>(); package2certs = new ConcurrentHashMap<>(); domains = Collections.synchronizedSet(new HashSet<ProtectionDomain>()); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance parallelLockMap = null; package2certs = new Hashtable<>(); domains = new HashSet<>(); assertionLock = this; } }
我们会发现,parentClassLoader就是getSystemClassLoader()返回的,
java.lang.ClassLoader#getSystemClassLoader java.lang.ClassLoader#initSystemClassLoader // 这个方法中的其他变量我们可以暂时先不关心,我们看到有获取到一个Launcher对象 private static synchronized void initSystemClassLoader() { if (!sclSet) { if (scl != null) throw new IllegalStateException("recursive invocation"); sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); if (l != null) { Throwable oops = null; // 在这里调用其getClassLoader()方法,将返回的值,赋值给scl,而这个scl就是入参中的parent scl = l.getClassLoader(); try { scl = AccessController.doPrivileged( new SystemClassLoaderAction(scl)); } catch (PrivilegedActionException pae) { oops = pae.getCause(); if (oops instanceof InvocationTargetException) { oops = oops.getCause(); } } if (oops != null) { if (oops instanceof Error) { throw (Error) oops; } else { // wrap the exception throw new Error(oops); } } } sclSet = true; } }
这里的getClassLoader()返回的就是上面我说明的,需要特别关注的this.classLoader这个全局变量
scl是从l.getClassLoader()这个方法获取到的结果,那我们看下这个方法
sun.misc.Launcher#getClassLoader public ClassLoader getClassLoader() { return this.loader; }
可以看到,这里的getClassLoader,就是博客上面 Launcher 这个方法中赋值的this.loader;
所以,通过上面的代码,可以发现,我们自定义的类解析器,是在初始化的时候,指定了parent为AppClassLoader
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。