Java类中热替换的概念、设计与实现(2)
编写自定义的ClassLoader
为了能够完全掌控类的加载过程,我们的定制类加载器需要直接从ClassLoader继承。首先我们来介绍一下ClassLoader类中和热替换有关的的一些重要方法。
◆findLoadedClass:每个类加载器都维护有自己的一份已加载类名字空间,其中不能出现两个同名的类。凡是通过该类加载器加载的类,无论是直接的还是间接的,都保存在自己的名字空间中,该方法就是在该名字空间中寻找指定的类是否已存在,如果存在就返回给类的引用,否则就返回null。这里的直接是指,存在于该类加载器的加载路径上并由该加载器完成加载,间接是指,由该类加载器把类的加载工作委托给其他类加载器完成类的实际加载。
◆getSystemClassLoader:Java2中新增的方法。该方法返回系统使用的ClassLoader。可以在自己
◆defineClass:该方法是ClassLoader中非常重要的一个方法,它接收以字节数组表示的类字节码,并把它转换成Class实例,该方法转换一个类的同时,会先要求装载该类的父类以及实现的接口类。
◆loadClass:加载类的入口方法,调用该方法完成类的显式加载。通过对该方法的重新实现,我们可以完全控制和管理类的加载过程。
◆resolveClass:链接一个指定的类。这是一个在某些情况下确保类可用的必要方法,详见Java语言规范中“执行”一章对该方法的描述。
了解了上面的这些方法,下面我们来实现一个定制的类加载器来完成这样的加载流程:我们为该类加载器指定一些必须由该类加载器直接加载的类集合,在该类加载器进行类的加载时,如果要加载的类属于必须由该类加载器加载的集合,那么就由它直接来完成类的加载,否则就把类加载的工作委托给系统的类加载器完成。
在给出示例代码前,有两点内容需要说明一下:1、要想实现同一个类的不同版本的共存,那么这些不同版本必须由不同的类加载器进行加载,因此就不能把这些类的加载工作委托给系统加载器来完成,因为它们只有一份。2、为了做到这一点,就不能采用系统默认的类加载器委托规则,也就是说我们定制的类加载器的父加载器必须设置为null。该定制的类加载器的实现代码如下:
- class CustomCL extends ClassLoader {
- private String basedir; // 需要该类加载器直接加载的类文件的基目录
- private HashSet dynaclazns; // 需要由该类加载器直接加载的类名
- public CustomCL(String basedir, String[] clazns) {
- super(null); // 指定父类加载器为 null
- this.basedir = basedir;
- dynaclazns = new HashSet();
- loadClassByMe(clazns);
- }
- private void loadClassByMe(String[] clazns) {
- for (int i = 0; i < clazns.length; i++) {
- loadDirectly(clazns[i]);
- dynaclazns.add(clazns[i]);
- }
- }
- private Class loadDirectly(String name) {
- Class cls = null;
- StringBuffer sb = new StringBuffer(basedir);
- String classname = name.replace('.', File.separatorChar) + ".class";
- sb.append(File.separator + classname);
- File classF = new File(sb.toString());
- cls = instantiateClass(name,new FileInputStream(classF),
- classF.length());
- return cls;
- }
- private Class instantiateClass(String name,InputStream fin,long len){
- byte[] raw = new byte[(int) len];
- fin.read(raw);
- fin.close();
- return defineClass(name,raw,0,raw.length);
- }
- protected Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- Class cls = null;
- cls = findLoadedClass(name);
- if(!this.dynaclazns.contains(name) && cls == null)
- cls = getSystemClassLoader().loadClass(name);
- if (cls == null)
- throw new ClassNotFoundException(name);
- if (resolve)
- resolveClass(cls);
- return cls;
- }
- }
在该类加载器的实现中,所有指定必须由它直接加载的类都在该加载器实例化时进行了加载,当通过loadClass进行类的加载时,如果该类没有加载过,并且不属于必须由该类加载器加载之列都委托给系统加载器进行加载。