Java类加载器
类与类加载器
类由加载它的类加载器一起确定其在Java虚拟机的唯一性。
换句话说,类只有在同一个类加载器加载的前提下才能有可能比较相等,不同的类加载器加载的类必然是不相等的。
双亲委派模型
启动类加载器(Bootstrap Class Loader)
负责加载<JAVA_HOME>\lib目录下,或者被-Xbootclasspath参数指定的路径中存放的,而且是Java虚拟机能够识别的类库到虚拟机的内存。识别是按照文件名称识别,文件名不符合的类库即使在lib目录也不会被加载。
启动类加载器不能被Java程序直接引用。
扩展类加载器(Extension Class Loader)
负责加载<JAVA_HOME>\lib\ext目录中,或者别java.ext.dirs系统变量所指定的路径中的所有的类库。
JDK的开发团队允许用户将具有通用性的类库放在ext目录下以扩展Java SE的功能。
可以被Java程序直接引用。
应用类加载器(Application Class Loader)
负责加载用户类路径(ClassPath)上所有的类库
可以直接引用
如果引用程序没有定义过自己的类加载器,一般情况下引用类加载器就是默认加载器。
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。
通常使用组合关系来复用父加载器的代码。
工作过程:如果一个类加载器收到了类加载请求,首先把请求委派给父类加载器去完成,每个层次都是如此,所有的加载请求最终都到达定测的启动类加载器,只有当父加载器无法完成加载请求,子加载器才尝试自己完成加载。
越基础的类由越上层的类加载器进行加载。
protected synchronized Class<?> loadClass(String name, boolen resolve) throws ClassNotFoundException
{
// 首先查找是否已经加载
Class c = findLoadedClass(name);
if (c == null){
try {
if (parent != null){
// 父类加载器
c = parent.loadClass(name, false);
} else {
// 启动类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null ) {
// 调用自定义类加载器,这里使用的不是loadClass而是findclass是有原因的
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
破坏双亲委派模型
- 双亲委派模型出现之前,已经存在的用户自定义类加载器
- 基础类型要调用用户代码,如JNDI、JDBC、JCE等
- 程序动态性:代码热替换、热部署
自定义类加载器
- 继承classloader
- 实现findClass()
- 如果要破坏双亲委派模型,就重载loadClass()方法,不再向父类委派,添加自己的逻辑
重点介绍一下OSGI
关键是自定义的类加载器机制,每个Bundle都有一个自己的类加载器,需要替换一个Bundle时,把Bundle连同类加载器一起换掉。类加载顺序如下:
- 将以java.*开头的类,委派给父类加载器加载。
- 否则,将委派列表名单内的类,委派给父类加载器加载。
- 否则,将Import列表中的类,委派给Export这个类的Bundle的类加载器加载。
- 否则,查找当前Bundle的ClassPath,使用自己的类加载器加载。
- 否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。
- 否则,查找Dynamic Import列表的Bundle,委派给对应的Bundle的类加载器加载。
- 否则,类查找失败。