zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

android classloader双亲委托模式详解手机开发

Android模式手机开发 详解 委托 ClassLoader 双亲
2023-06-13 09:20:14 时间

ClassLoader的双亲委托模式:classloader 按级别分为三个级别:最上级 : bootstrap classLoader(根类加载器) ; 中间级:extension classLoader (扩展类加载器) 最低级 app classLoader(应用类加载器)。

根(Bootstrap)类加载器:该加载器没有父加载器。它负责加载虚拟机的核心类库,如java.lang.*等。例如java.lang.Object就是由根类加载器加载的。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。根类加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,它并没有继承java.lang.ClassLoader类。

扩展(Extension)类加载器:它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库,如果把用户创建的JAR文件放在这个目录下,也会自动由扩展类加载器加载。扩展类加载器是纯Java类,是java.lang.ClassLoader类的子类。

系统(System)类加载器:也称为应用类加载器,它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器。系统类加载器是纯Java类,是java.lang.ClassLoader类的子类。
父子加载器并非继承关系,也就是说子加载器不一定是继承了父加载器。

对于Java来说,java 虚拟机要将被用到的java类文件通过classLoader 加载到JVM内存中,而这个classloader就是bootstrap classloader。而对于APP而言,首先请求app 级来加载,继而请求extension classLoader,最后启动bootstrap classLoader 。

自定义ClassLoader

这里写图片描述

public class MyClassLoader extends ClassLoader { 

 //类加载器名称 

 private String name; 

 //加载类的路径 

 private String path = "D:/"; 

 private final String fileType = ".class"; 

 public MyClassLoader(String name){ 

 //让系统类加载器成为该 类加载器的父加载器 

 super(); 

 this.name = name; 

 public MyClassLoader(ClassLoader parent, String name){ 

 //显示指定该类加载器的父加载器 

 super(parent); 

 this.name = name; 

 public String getPath() { 

 return path; 

 public void setPath(String path) { 

 this.path = path; 

 @Override 

 public String toString() { 

 return this.name; 

 private byte[] loaderClassData(String name){ 

 InputStream is = null; 

 byte[] data = null; 

 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

 this.name = this.name.replace(".", "/"); 

 try { 

 is = new FileInputStream(new File(path + name + fileType)); 

 int c = 0; 

 while(-1 != (c = is.read())){ 

 baos.write(c); 

 data = baos.toByteArray(); 

 } catch (Exception e) { 

 e.printStackTrace(); 

 } finally{ 

 try { 

 is.close(); 

 baos.close(); 

 } catch (IOException e) { 

 e.printStackTrace(); 

 return data; 

 @Override 

 public Class ? findClass(String name){ 

 byte[] data = loaderClassData(name); 

 return this.defineClass(name, data, 0, data.length); 

 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 

 //loader1的父加载器为系统类加载器 

 MyClassLoader loader1 = new MyClassLoader("loader1"); 

 loader1.setPath("D:/lib1/"); 

 //loader2的父加载器为loader1 

 MyClassLoader loader2 = new MyClassLoader(loader1, "loader2"); 

 loader2.setPath("D:/lib2/"); 

 //loader3的父加载器为根类加载器 

 MyClassLoader loader3 = new MyClassLoader(null, "loader3"); 

 loader3.setPath("D:/lib3/"); 

 Class clazz = loader2.loadClass("Sample"); 

 Object object = clazz.newInstance(); 

public class Sample { 

 public Sample(){ 

 System.out.println("Sample is loaded by " + this.getClass().getClassLoader()); 

 new A(); 

public class A { 

 public A(){ 

 System.out.println("A is loaded by " + this.getClass().getClassLoader()); 

}

每一个自定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前 ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个 ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloade。

上面讲解了一下ClassLoader的作用以及一个最基本的加载流程,接下来我们说说ClassLoader使用了双亲委托模式进行类加载。

ClassLoader 双亲委托模式

通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

为了更好的理解双亲委托模式,我们先自定义一个ClassLoader,假设我们使用这个自定义的ClassLoader加载 java.lang.String,那么这里String是否会被这个ClassLoader加载呢?

事实上java.lang.String这个类并不会被我们自定义的classloader加载,而是由bootstrap classloader进行加载,为什么会这样?实际上这就是双亲委托模式的原因,因为在任何一个自定义ClassLoader加载一个类之前,它都会先 委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载。而在上面的例子中,因为 java.lang.String是属于java核心API的一个类,所以当使用自定义的classloader加载它的时候,该 ClassLoader会先委托它的父亲ClassLoader进行加载(bootstrap classloader),所以并不会被我们自定义的ClassLoader加载。

我们来看一下ClassLoader的一段源码:

protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ 

 // 首先检查该name指定的class是否有被加载 

 Class c = findLoadedClass(name); 

 if (c == null) { 

 try { 

 if (parent != null) { 

 //如果parent不为null,则调用parent的loadClass进行加载 

 c = parent.loadClass(name, false); 

 }else{ 

 //parent为null,则调用BootstrapClassLoader进行加载 

 c = findBootstrapClass0(name); 

 }catch(ClassNotFoundException e) { 

 //如果仍然无法加载成功,则调用自身的findClass进行加载 

 c = findClass(name); 

 if (resolve) { 

 resolveClass(c); 

 return c; 

 }
使用双亲委托模式优点

那么我们使用双亲委托模式有什么好处呢?

因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。

附:Android ClassLoader简介

5865.html

app程序应用开发手机开发无线开发移动端开发