美文网首页Java高级
Java高级---类加载ClassLoader

Java高级---类加载ClassLoader

作者: 初夏的雪 | 来源:发表于2020-12-10 11:55 被阅读0次

    任何一个java程序都是有一个或多个class文件组成的,在程序运行时,需要将class文件加载到JVM虚拟机中才可以用,负责加载这些class文件的就是java的类加载机制。

    简单来说:就是加载class文件,提供给程序运行时使用。

    知识点

    1)基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节码,然后从这些字节码代码中定义出一个java类的实例。

    2)工作原理则是通过双亲委托机制来实现的,这也可以避免重复加载,保证类的唯一性,且java类随着他的类加载器一起具备了一种带有优先级的层次关系。

    ​ 如何保证类的唯一性呢?

    JVM在判断两个class是否相同时,不仅仅要判断两个类名是否相同,还要判断负责加载这两个类的加载器是否一样,也就是说同一个类加载器加载相同名称的类,虚拟机才会认为加载的是同一个类,否则的话认为他们是不同的类。

    ClassLoader是一个抽象类,他在android中的具体实现类有:

    **1. BootClassLoader:**        用于加载android Framework层的class文件
    
    **2. PathClassLoader:**        用于加载android 应用程序中的类加载器,可以是指定的dex,jar,  zip ,  apk中的class.dex
    
    **3. DexClassLoader:**         用于加载指定的dex ,jar ,zip ,apk中的classes.dex
    
    类加载器类图

    了解上面的一些基础概念理论的东西后,我们用一张流程图来分析一下类加载器的工作原理:


    类加载的工作原理流程图

    知识点:

    类加载的双亲委托机制

    ​ 类加载器在尝试自己去查找某一个类的字节码时,会先就这个任务交给他的父类加载器去尝试加载这个类,而父类加载器也先交给父类的父类加载器去执行,依次递归查找。如果父类找到了则直接返回,如果父类没有找到则再由类加载器自己查找,这种查找的方式就是双亲委托机制。

    剖析源码

    ​ 解析来我们来从源码角度一步一步潜入类加载器中学习一下,其他类加载器的几个类都比较简单,相对复杂的是在ClassLoader.java中。废话不多说,直接开撸。。。。。。。

    1. BaseDexClassLoader.java

    public class BaseDexClassLoader extends ClassLoader {
    32
    33    /**
    34     * Hook for customizing how dex files loads are reported.
    35     *
    36     * This enables the framework to monitor the use of dex files. The
    37     * goal is to simplify the mechanism for optimizing foreign dex files and
    38     * enable further optimizations of secondary dex files.
    39     *
    40     * The reporting happens only when new instances of BaseDexClassLoader
    41     * are constructed and will be active only after this field is set with
    42     * {@link BaseDexClassLoader#setReporter}.
    43     */
    44    /* @NonNull */ private static volatile Reporter reporter = null;
    45
    46    private final DexPathList pathList;
    47
    48    /**
    49     * Constructs an instance.
    50     * Note that all the *.jar and *.apk files from {@code dexPath} might be
    51     * first extracted in-memory before the code is loaded. This can be avoided
    52     * by passing raw dex files (*.dex) in the {@code dexPath}.
    53     *
    54     * @param dexPath the list of jar/apk files containing classes and
    55     * resources, delimited by {@code File.pathSeparator}, which
    56     * defaults to {@code ":"} on Android.
    57     * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26.
    58     * @param librarySearchPath the list of directories containing native
    59     * libraries, delimited by {@code File.pathSeparator}; may be
    60     * {@code null}
    61     * @param parent the parent class loader
    62     */
    63    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
    64            String librarySearchPath, ClassLoader parent) {
    65        this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
    66    }
    67
    68    /**
    69     * @hide
    70     */
    71    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
    72            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
    73        super(parent);
                //存放dex的路径
    74        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    75
    76        if (reporter != null) {
    77            reportClassLoaderChain();
    78        }
    79    }
    80
    81  
    113    /**
    114     * Constructs an instance.
    115     *
    116     * dexFile must be an in-memory representation of a full dexFile.
    117     *
    118     * @param dexFiles the array of in-memory dex files containing classes.
    119     * @param parent the parent class loader
    120     *
    121     * @hide
    122     */
    123    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
    124        // TODO We should support giving this a library search path maybe.
    125        super(parent);
    126        this.pathList = new DexPathList(this, dexFiles);
    127    }
    128
    129    @Override
    130    protected Class<?> findClass(String name) throws ClassNotFoundException {
    131        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    132        Class c = pathList.findClass(name, suppressedExceptions);
    133        if (c == null) {
    134            ClassNotFoundException cnfe = new ClassNotFoundException(
    135                    "Didn't find class \"" + name + "\" on path: " + pathList);
    136            for (Throwable t : suppressedExceptions) {
    137                cnfe.addSuppressed(t);
    138            }
    139            throw cnfe;
    140        }
    141        return c;
    142    }
    281}
    

    2. PathClassLoader.java

    public class PathClassLoader extends BaseDexClassLoader {
    26    /**
    27     * Creates a {@code PathClassLoader} that operates on a given list of files
    28     * and directories. This method is equivalent to calling
    29     * {@link #PathClassLoader(String, String, ClassLoader)} with a
    30     * {@code null} value for the second argument (see description there).
    31     *
    32     * @param dexPath the list of jar/apk files containing classes and
    33     * resources, delimited by {@code File.pathSeparator}, which
    34     * defaults to {@code ":"} on Android
    35     * @param parent the parent class loader
    36     */
    37    public PathClassLoader(String dexPath, ClassLoader parent) {
    38        super(dexPath, null, null, parent);
    39    }
    40
    41    /**
    42     * Creates a {@code PathClassLoader} that operates on two given
    43     * lists of files and directories. The entries of the first list
    44     * should be one of the following:
    45     *
    46     * <ul>
    47     * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
    48     * well as arbitrary resources.
    49     * <li>Raw ".dex" files (not inside a zip file).
    50     * </ul>
    51     *
    52     * The entries of the second list should be directories containing
    53     * native library files.
    54     *
    55     * @param dexPath the list of jar/apk files containing classes and
    56     * resources, delimited by {@code File.pathSeparator}, which
    57     * defaults to {@code ":"} on Android
    58     * @param librarySearchPath the list of directories containing native
    59     * libraries, delimited by {@code File.pathSeparator}; may be
    60     * {@code null}
    61     * @param parent the parent class loader
    62     */
    63    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
    64        super(dexPath, null, librarySearchPath, parent);
    65    }
    66}
    
    1. DexClassLoader.java

    public class DexClassLoader extends BaseDexClassLoader {
    36    /**
    37     * Creates a {@code DexClassLoader} that finds interpreted and native
    38     * code.  Interpreted classes are found in a set of DEX files contained
    39     * in Jar or APK files.
    40     *
    41     * <p>The path lists are separated using the character specified by the
    42     * {@code path.separator} system property, which defaults to {@code :}.
    43     *
    44     * @param dexPath the list of jar/apk files containing classes and
    45     *     resources, delimited by {@code File.pathSeparator}, which
    46     *     defaults to {@code ":"} on Android
    47     * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26.
    48     * @param librarySearchPath the list of directories containing native
    49     *     libraries, delimited by {@code File.pathSeparator}; may be
    50     *     {@code null}
    51     * @param parent the parent class loader
    52     */
    53    public DexClassLoader(String dexPath, String optimizedDirectory,
    54            String librarySearchPath, ClassLoader parent) {
    55        super(dexPath, null, librarySearchPath, parent);
    56    }
    57}
    
    1. ClassLoader.java

     protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
                // First, check if the class has already been loaded
                //从JVM缓存中已经加载的Class中进行查找,已经加载过,则直接返回   
         //findLoadedClass返回会先判断当前的类加载器是否是BootClassLoader?如果不是,那么就将当前的加载器传递给VMClassLoader : 如果是,则给VMClassLoader传递空值进去
    //     最后再通过native方法在缓存中查找
         
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    //父类加载器加载
                    try {
                        if (parent != null) {
                            //双亲委托,递归交给父类加载器进行加载,
                            c = parent.loadClass(name, false);
                        } else {
                            //此方法返回null
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    //自己进行加载
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        //findClass 系统提供的是会直接抛出异常   throw new ClassNotFoundException(name)
                        //我们可以在自己的类加载器中重写findClass方法,来做到我们自己去查找加载类的功能
                        c = findClass(name);
                    }
                }
                return c;
        }
    

    以上就是笔者想要和大家分享的关于类加载器的一些内容,那么我们实际工作中该如何使用呢?持续完善中。。。。。。

    相关文章

      网友评论

        本文标题:Java高级---类加载ClassLoader

        本文链接:https://www.haomeiwen.com/subject/okbhgktx.html