Java ClassLoader简介
java 开发中编写的都是.java的对象文件,当我们需要运行当前的项目的时候,当前项目会被编译,编译后需要用到的.java文件就会被被编译成.class文件。但是Java程序 .class文件并不是本地可执行的,当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做ClassLoader。
JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,Bootstra pClassLoader是用C++语言实现的。另外JVM还会提供了ExtClassLoader和AppClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载。
JDK提供的三个ClassLoader,根据层级从高到低为:
- BootstrapClassLoader ,主要加载JVM自身工作需要的类。
- ExtClassLoaderr,主要加载%JAVA_HOME%\lib\ext目录下的库类。
- AppClassLoader,主要加载Classpath指定的库类,一般情况下这是程序中的默认类加载器,也是ClassLoader.getSystemClassLoader() 的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)
Android ClassLoader简介
android应用程序,本质上使用的是java开发,使用标准的java编译器编译出Class文件,和普通的java开发不同的地方是把class文件再重新打包成dex类型的文件。Android虚拟机运行的dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并,优化,然后生成一个最终的class.dex。
加载这样特殊的.dex文件就需要特殊的类装载器,所以android中提供了DexClassLoader类。
Android系统中类加载器有:
- BootClassLoader,Android系统启动时会使用BootClassLoader来预加载常用类,它是由java实现的。
- PathClassLoader,Android系统使用PathClassLoader来加载系统类和应用程序的类
- DexClassLoader,可以加载dex文件以及包含dex的apk文件或jar文件
PathClassLoader和DexClassLoader继承自BaseDexClassLoader,并且它们具体方法实现都在BaseDexClassLoader中。
Android系统使用PathClassLoader来加载系统类和应用程序的类,如果是加载非系统应用程序类,则会加载data/app/目录下的dex文件以及包含dex的apk文件或jar文件。PathClassLoader不建议开发直接使用。
libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
//* dexPath:dex文件以及包含dex的apk文件或jar文件的路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’。
//* librarySearchPath:包含 C/C++ 库的路径集合,多个路径用文件分隔符分隔分割,可以为null。
//* parent:ClassLoader的parent。
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
DexClassLoader可以加载dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载,这也就意味着DexClassLoader可以在应用未安装的情况下加载dex相关文件。因此,它是热修复和插件化技术的基础。来查看它的代码,如下所示。
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
//多一个optimizedDirectory参数
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
DexClassLoader构造方法的参数要比PathClassLoader多一个optimizedDirectory参数,参数optimizedDirectory代表什么呢?我们知道应用程序第一次被加载的时候,为了提高以后的启动速度和执行效率,Android系统会对dex相关文件做一定程度的优化,并生成一个ODEX文件,此后再运行这个应用程序的时候,只要加载优化过的ODEX文件就行了,省去了每次都要优化的时间,而参数optimizedDirectory就是代表存储ODEX文件的路径,这个路径必须是一个内部存储路径。
PathClassLoader没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的路径为:/data/dalvik-cache
。
ClassLoader双亲委托机制
JVM以及Dalvik均是通过ClassLoader来加载类的,加载类的实现方式我们称为 双亲委托模型。
如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载。
java中ClassLoader加载顺序
双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。
假设有一个开发者自己编写了一个名为java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义ClassLoader来加载自己编写的java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在BootstrapClassLoader的路径下找到java.lang.Object类,并载入它。
双亲委托的代码实现java.lang.ClassLoader#loadClass,系统自带的其他的子类都继承了此方法且没有进行复写。
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
//判断该类有没有加载各国
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
//先尝试由父ClassLoader去加载
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
//如果父ClassLoader没找到该类,调用自己的findClass方法。
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
参考:
http://blog.csdn.net/markzy/article/details/53192993
https://www.cnblogs.com/linux007/p/5782677.html
http://blog.csdn.net/Mr_LiaBill/article/details/50497055
http://blog.csdn.net/itachi85/article/details/78088701
网友评论