Java类中热替换
类的热替换是Java在线升级系统设计中的基础技术,从文中给出的实例来看,构建在线升级系统不仅仅是一个技术问题,还牵扯到很多管理方面的因素,比如:如何管理、部署系统中的可在线升级部分和不可在线升级部分以降低系统的管理、维护成本等。
对于许多关键性业务或者庞大的Java系统来说,如果必须暂停系统服务才能进行系统升级,既会大大影响到系统的可用性,同时也增加了系统的管理和维护成本。因此,如果能够方便地在不停止系统业务的情况下进行系统升级,则可以很好地解决上述问题。
JavaClassLoader技术剖析
要构建在线升级系统,一个重要的技术就是能够实现Java类的热替换——也就是在不停止正在运行的系统的情况下进行类(对象)的升级替换。而Java的ClassLoader正是实现这项技术的基础。
在Java中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分为显式加载和隐式加载。大家使用new关键字创建类实例时,其实就隐式地包含了类的加载过程。对于类的显式加载来说,比较常用的是Class.forName。其实,它们都是通过调用ClassLoader类的loadClass方法来完成类的实际加载工作的。直接调用ClassLoader的loadClass方法是另外一种不常用的显式加载类的技术。
图1.Java类加载器层次结构图
ClassLoader在加载类时有一定的层次关系和规则。在Java中,有四种类型的类加载器,分别为:BootStrapClassLoader、ExtClassLoader、AppClassLoader以及用户自定义的ClassLoader。这四种类加载器分别负责不同路径的类的加载,并形成了一个类加载的层次结构。
BootStrapClassLoader处于类加载器层次结构的最高层,负责sun.boot.class.path路径下类的加载,默认为jre/lib目录下的核心API或-Xbootclasspath选项指定的jar包。ExtClassLoader的加载路径为java.ext.dirs,默认为jre/lib/ext目录或者-Djava.ext.dirs指定目录下的jar包加载。AppClassLoader的加载路径为java.class.path,默认为环境变量CLASSPATH中设定的值。也可以通过-classpath选型进行指定。用户自定义ClassLoader可以根据用户的需要定制自己的类加载过程,在运行期进行指定类的动态实时加载。
这四种类加载器的层次关系图如图1所示。一般来说,这四种类加载器会形成一种父子关系,高层为低层的父加载器。在进行类加载时,首先会自底向上挨个检查是否已经加载了指定类,如果已经加载则直接返回该类的引用。如果到最高层也没有加载过指定类,那么会自顶向下挨个尝试加载,直到用户自定义类加载器,如果还不能成功,就会抛出异常。Java类的加载过程如图2所示。
图2.Java类的加过程
每个类加载器有自己的名字空间,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。不管该类有没有变化,下次再需要加载时,它只是从自己的缓存中直接返回已经加载过的类引用。
我们编写的应用类默认情况下都是通过AppClassLoader进行加载的。当我们使用new关键字或者Class.forName来加载类时,所要加载的类都是由调用new或者Class.forName的类的类加载器(也是AppClassLoader)进行加载的。要想实现Java类的热替换,首先必须要实现系统中同名类的不同版本实例的共存,通过上面的介绍我们知道,要想实现同一个类的不同版本的共存,我们必须要通过不同的类加载器来加载该类的不同版本。另外,为了能够绕过Java类的既定加载过程,我们需要实现自己的类加载器,并在其中对类的加载过程进行完全的控制和管理