Java高级---类加载ClassLoader
2020-12-10 本文已影响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中。废话不多说,直接开撸。。。。。。。
-
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}
-
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}
-
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;
}
以上就是笔者想要和大家分享的关于类加载器的一些内容,那么我们实际工作中该如何使用呢?持续完善中。。。。。。