第五部分.Java2中对类加载器的改进
概览
在Java1.2和以后的版本中, 类加载器有了很大的改进。以前的代码仍然可以工作,但是新的系统让我们的实现更容易。这种新模型就是代理委托模型,就是说如果这个类加载器找不到某个类,它会让他的父类加载器来找。系统类加载器是所有类加载器的祖先, 系统类加载器通过默认的方式加载类--也就是从本地文件系统中加载。覆盖loadClass方法一般都尝试几种方式来加载类,如果你写了很多类加载器,你会发现你只是一次又一次在这个复杂的方法中作一些修改而已。Java1.2种loadClass的默认实现包含了寻找类的最普通的途径,允许你覆盖 findClass方法,loadClass在适当的是否调用findClass方法。这样做的好处是你不需要覆盖loadClass,你只需要覆盖 findClass,这样可以减少工作量。
新增方法: findClass
这个方法被loadClass的默认实现调用。findClass的目标是包含所有类加载器特定的代码,而不需要重复这些代码(比如在指定的方法失败的时候调用系统类加载器)。
新增方法: getSystemClassLoader
不论你是否覆盖方法findClass和loadClass, 方法getSystemClassLoader都可以直接访问系统类加载器(而不是通过findSystemClass间接的访问)。
新增方法: getParent
为了把请求委托给父类加载器,通过这个方法可以获得这个类加载器的父类加载器。当自定义类加载器中的特定方法无法找到类的时候你可能把请求委托给父类加载器。类加载器的父类加载器包含创建这个类加载器的代码。
第六部分. 源代码
CompilingClassLoader.java
以下是文件CompilingClassLoader.java内容
import java.io.*;
/*
CompilingClassLoader动态的编译Java源文件。它检查.class文件是否存在,.class文件是否比源文件陈旧。
*/
public class CompilingClassLoader extends ClassLoader
{
// 指定一个文件名,从磁盘读取整个文件内容,返回字节数组。
private byte[] getBytes( String filename ) throws IOException {
// 获得文件大小。
File file = new File( filename );
long len = file.length();
//创建一个数组刚好可以存放文件的内容。
byte raw[] = new byte[(int)len];
// 打开文件
FileInputStream fin = new FileInputStream( file );
// 读取所有内容,如果没法读取,表示发生了一个错误。
int r = fin.read( raw );
if (r != len)
throw new IOException( "Can't read all, "+r+" != "+len );
// 别忘了关闭文件。
fin.close();
// 返回这个数组。
return raw;
}
// 产生一个进程来编译指定的Java源文件,制定文件参数.如果编译成功返回true,否者,
// 返回false。
private boolean compile( String javaFile ) throws IOException {
// 显示当前进度
System.out.println( "CCL: Compiling "+javaFile+"..." );
// 启动编译器
Process p = Runtime.getRuntime().exec( "javac "+javaFile );
// 等待编译结束
try {
p.waitFor();
} catch( InterruptedException ie ) { System.out.println( ie ); }
// 检查返回码,看编译是否出错。
int ret = p.exitValue();
// 返回编译是否成功。
return ret==0;
}