zoukankan      html  css  js  c++  java
  • android classloader双亲托付模式

    概述

    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;  
        }

    使用双亲托付模式长处

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

    1. 由于这样能够避免反复载入。当父亲已经载入了该类的时候,就没有必要子ClassLoader再载入一次。

    2. 考虑到安全因素,我们试想一下,假设不使用这样的托付模式,那我们就能够随时使用自己定义的String来动态替代java核心api中定义类型,这样会存在很大的安全隐患。而双亲托付的方式,就能够避免这样的情况,由于String已经在启动时被载入,所以用户自己定义类是无法载入一个自己定义的ClassLoader。

    附:Android ClassLoader简单介绍

  • 相关阅读:
    10种 分布式ID生成方式(新增MongoDB的ObjectId)
    Spring核心接口Ordered的实现及应用 (动态切换数据源时候用到)
    No module named 'Crypto' 解决方案
    使用Anaconda管理多个版本的Python环境
    深入浅出Blazor webassembly 之API服务端保护
    [转载]HTTPS 是如何保护你的安全的
    [转载]api接口token的生成和应用
    深入浅出Blazor webassembly之HttpClient使用
    深入浅出Blazor webassembly之自定义Input组件
    深入浅出Blazor webassembly之EditForm
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7295018.html
Copyright © 2011-2022 走看看