开发者

自定义类加载器的父类为何是AppClassLoader说明

开发者 https://www.devze.com 2022-11-28 15:06 出处:网络 作者: 小小少年_
目录首先有一个概念要了解Launcher构造一个自定义类加载器newMyClassLoaderTest()首先有一个概念要了解我们通常说在jdk中,默认有三个类加载器,bootstrapClassL...
目录
  • 首先有一个概念要了解
  • Launcher
  • 构造一个自定义类加载器
  • new MyClassLoaderTest()

首先有一个概念要了解

我们通常说在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) {
开发者_Go开发      } 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 gphpetLoadPath() {
    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 + "/" + pythonclassName + ".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.ClasphpsLoader#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 SysteHLzUnkTmClassLoaderAction(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;
  }
}

这里android的getClassLoader()返回的就是上面我说明的,需要特别关注的this.classLoader这个全局变量

scl是从l.getClassLoader()这个方法获取到的结果,那我们看下这个方法

sun.misc.Launcher#getClassLoader
public ClassLoader getClassLoader() {
    return this.loader;
}

可以看到,这里的getClassLoader,就是博客上面 Launcher 这个方法中赋值的this.loader;

所以,通过上面的代码,可以发现,我们自定义的类解析器,是在初始化的时候,指定了parent为AppClassLoader

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号