利用ClassLoader实现动态热布署

本文介绍如何使用自定义ClassLoader实现Java应用的热部署,通过不停机更新解决系统维护和升级问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大型应用中,系统是不能轻易停机的,一般选择升级都是选在晚上人少的时候进行停机维护。这样大大的降低了系统的可用性,也提高了系统维护的成本。因此如果能够实现在不停机的情况下能够进行系统的维护或者升级,则能够很好的解决上面的情况。在JAVA中我们可以利用CustomerClassLoader来实现以上机制。

在开始之前先简介一下JAVA中的ClassLoader机制。Java中类的实例化分为两部分:类的加载与类的实例化。而类的加载一般分为两种情况,显式加载和隐式加载。显示加载就是我们使用Class.forName而隐式加载的话就是我们平常使用最多的new.new关键字会在后台为我们做一些工作。

JAVA的类加载机制是分为几个层次来加载的。首先我们来看一下JAVA的加载器的层次结构:

ClassLoad Struct

1,BootStrapClassLoader是根加载器,它默认加载的是jre/lib目录下面的jar.或者是你在jdk的启动参数里面指定的:-XbootClasspath

2,ExtClassLoader加载器,它默认加载的是jre/lib/ext目录下面的jar,或者是启动参数里-Djava.ext.dirs 指定的目录

3,AppClassLoader加载器,它默认加载的是classpath变量指定的目录,其实就是我们应用程序的class目录。

4,CustomeClassLoader加载器,这是我们自已定义的加载器,必须要继承ClassLoader类。可以根据用户的需要定制自己的类加载过程,在运行期进行指定类的动态实时加载

而JDK的加载过程分为两个过程,第一步是:从下到上检查加载器的命名空间内(每个class在命名空间内只存在有一份)是否已有要加载的Class.如果找到则直接返回其引用,如果没有。则从上到下从各自的目录下去加载指定的class.如果到了最底层还没有加载成功。则抛出noclassfound的异常。其加载流程,详见如下图:

接下来,我们初步的了解了JDK的ClassLoader加载机制后,可见我们要实现动态的热布署,我们可以用CustomerClassLoader来实现我们的功能。自定义的ClassLoader需求要继承ClassLoader类。下面对于ClassLoader中的几个重要方法做一下介绍。

findLoadedClass:从当前类加载器的命名空间内查找指定的class,如果存在则返回其引用。不存在则返回null

getSystemClassLoader:调用系统使用的加载器,有时候我们自定义的类加载器可以调用它来执行一些其它的事情

resolveClass:链接一个指定的类,在某些情况下保证这个类一定可以用

defineClass:从class字节文件生成一个class对象

loadClass:加载类的入口方法,显式的加载类的方法。

下面我们看一下实现的自定义的类加载器:

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /* * create date: 2010-1-26 * Project Name: TestClassLoader * package: * */ public class HostClassLoader extends ClassLoader { private String className; public HostClassLoader(String className) throws IOException { super(null); this.className=className; loadFile(); } public Class loadFile() throws IOException{ InputStream is=null; byte[] b=null; File file=null; try { file=new File(className+".class"); b=new byte[(int) file.length()]; is=new FileInputStream(file); is.read(b); return defineClass(className, b, 0, b.length); } catch (FileNotFoundException e) { // TODO 自动生成 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成 catch 块 e.printStackTrace(); } finally{ is.close(); } return null; } protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class cls=findLoadedClass(name); if(cls==null){ cls=getSystemClassLoader().loadClass(name); } if(resolve){ resolveClass(cls); } return cls; }

在以上代码段中,我们在构造方法中,调用了super(null),这样的做法就是让我们定制的加载器的父加载器为null.这样避免我们定义的类被父加载器(AppClassLoader)抢先加载。在loadClass中我们首先从自已的类加载器的命名空间内查找要加载的类。如果没有找到。则让系统的加载器加载。

/* * create date: 2010-1-26 * Project Name: TestClassLoader * package: * */ public class Foo { public void say(){ System.out.println("hello version 1"); } }

调用的类:

import java.lang.reflect.Method; public class TestMain { public void run() { try { HostClassLoader classLoader=new HostClassLoader("Foo"); Class cls=classLoader.loadClass("Foo"); Object obj=cls.newInstance(); Method m=obj.getClass().getMethod("say",new Class[]{}); m.invoke(obj,new Object[]{}); } catch (Exception e) { // TODO 自动生成 catch 块 e.printStackTrace(); } } /** * * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO 自动生成方法存根 int i=0; while(i<1000){ TestMain tm=new TestMain(); tm.run(); Thread.sleep(3000); i++; } } }

在以上类中,我们让系统每隔三秒种调用一次run程序。然后运行我们系统,可以看到控制台打出如下语句:
hello version 1
hello version 1

热下来我们不要停我们的应用,直接修改Foo的代码如下:

可见修改后打出如下语句:

hello version 1
hello version 1
hello version 1
hello version 2
hello version 2

代表已经替换成功了。

注意:有朋友会说为什么不把上面的反射的代码改成

Foo foo = (Foo) cls.newInstance(); foo.say();

这样呢,这样更明了,但是这种情况下。系统会报错:ClassCastException,因为你强转的时候的Foo是由AppClassLoader加载的,而我们的foo是由自定义的加载器加载的。在JDK中,继使两个类的类型相同,但是由不同的加载器加载的话。虚拟机也会认为这是不同的类型。但是我们可以通过接口来实现。是不会报错的,可以改成如下代码,则可以运行通过:

Ifoo foo = (Ifoo) cls.newInstance(); foo.say();

以上代码中Ifoo是Foo接口。
当然,要实现一个在线自动升级热布署的系统光靠上面这一点代码是不够的,还需要有完善的回退机制,检查机制等。但是以上部分做为core部分,基于以上部分是完全可以打造一个不停机升级的热布署应用。解决大型应用中的维护和升级的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值