8.1.16 使用1.1版本的用户自定义类装载器

loadCIass ()方法从检查被请求调用的类型是否已经被自己装载过了开始。这是通过调用 findLoadedClass ()实现的,后者是ClassLoader的一个方法,传递被请求的类型的全限定名作为参数。如果这个类装载已经被标记为是这个具有该全限定名的类型的初始类装载器, findLoadedClass ()就会返回表示这个类型的Class实例。

// Check the loaded class cache
result=findLoadedClass(className);
if(result!=null) {
//Return a cached class;
return result;
}

本章前面曾提到过,虚拟机会为每一个类装载器维护一张列表,列表中是已经被请求过的类型的名字。这些列表包含了每一个类装载器被标记为初始类装载器的类型,它们代表了每一 个类装载器的命名空间里当前填充的惟一名字的集合。当在解析CONSTANT_Class_info人口的步骤la装载类的时候(在本章前面讲到的),虚拟机总是会在调用loadCIass ()之前检查这个内 部列表。这样,虚拟机永远不会自动在同一个用户自定义类装载器上调用同一个名字的类型两次。然而,GreeterClassLoader调用了findLoadedClass ()用内部已装载的类型名字列表检查被 请求的类型,这是为什么呢?因为,虽然虚拟机永远不会要求用户自定义的类装载器装载两次 同样的类,程序却可能这样做。

举例来说,假设Greet程序是这样被命令行调用的:

java Greet greeters Hello Hello Bello Hello Hello

在这个命令行中,Greet程序会使用同样的名字Hello在同一个GreeterClassLoader类装载器对 象上调用loadClass ()五次。第一次,GreeterClassLoader会装载这个类;但是以后的4次, GreeterClassLoader会通过调用findLoadedClass ()简单地把Hello类的Class实例返回。它只会装载Hello类一次。

如果loadClass ()方法认定被请求的类型没有被装载进它的命名空间,它下一步会传递被请求的类型给findSystemClass ():
// Check with the primordial class loader
try {

result = super.findSystemClass(className);

// Return a system class

return result;

}catch (ClassNotFoundException e) {

}

当findSystemClass ()方法在1.1版虚拟机中被调用的时候,原始的类装载器试图装载这个 类型。在版本1.2中,系统类装载器试图装载这个类型。如果装载成功了,findSystemClass () 会返回代表这个类型的Class实例,loadClass ()返回同样的Class实例。

如果原始的类装载器(在版本1.1中)或者系统的类装载器(在版本1.2中)不能够装载这个 类型,findSystemClass ()就抛出ClassNotFoundError异常。在这个例子中,loadClass ()方法 检查确认被请求的类不是java包的一部分:这个检查可以防止标准的java包(java.lang, java.io,等等)被除了启动类装载器之外的其 他类装载器装载。
在第3章中讲过,在同样的命名包中声明的两个类型只能拥有访问各自包内可 视的成员,假如它们属于同一个运行时包(如果它们被同一个类装载器装载)。但是“运行时包” 这个概念及其在可访问性上的作用是在Java虚拟机规范第2版中才引入的。也就是说,早期版本 的类装载器不得不自行显式地防止用户自定义的类装载器试图装载某些类型,它们声明自己是 Java API (或者任何其他“受限的”包)的一部分,但是又不能被启动类装载器装载。

如果类名字不是由java.开始的,loadClass ()方法下面会调用getTypeFromBasePath (), getTypeFromBasePath试图用用户自定义的类装载器的定制方式装载二进制数据。

//Try to load it from the basePath diroctory.

classData = getTypeFromBasePath (className);

if (classData==null) {
throw new ClassNotFoundException();
}

getTypeFromBasePath ()方法在基础路径中查找类型名带有“.class”后缀的文件。(这个 基础路径是传递到GreeterClassLoader的构造方法中的。)如果genTypeFromBasePath ()方法无 法找到这个文件,它返回一个null结果,并且loadClass ()方法抛出ClassNotFoundException异常。否则,loadClass ()使用getTypeFromBasePath ()返回的字节数组作为参数调用 defineClass ():
//Parse it

result= defineClass(className, classData, 0, classData.length);
if (result==null) {
System.out.printIn("OCL - Class format error: "
+ className);
throw new ClassFormatError();

}

defineClass()方法通过以下步骤完成装载过程:即解析二进制数据到内部数据格式,并且 创建一个Class实例。defineClass ()方法并不连接和初始化类型。(在本章前面提到过, defineClass ()方法也会确认所有该类型的超类型也被装载。这是通过在每一个直接超类和超接 口上调用这个用户自定义的类装载器的loadClass()方法实现的,层次结构中的所有超类型的 解析过程是一个递归过程。)

如果defineClass ()成功了,loadClass ()方法检査resolve参数是否是true。如果是,它调 用resolveClass (),把defineClass ()返回的Class实例作为参数。resolveClass ()方法连接该类。最终,loadClass ()返回新创建的Class实例:
if (resolvelt) {

posted @ 2019-12-03 21:54  mongotea  阅读(172)  评论(0编辑  收藏  举报