Java类中热替换的概念、设计与实现(3)

实现Java类的热替换

现在来介绍一下我们的实验方法,为了简单起见,我们的包为默认包,没有层次,并且省去了所有错误处理。要替换的类为Foo,实现很简单,仅包含一个方法sayHello:

 
 
  1. public class Foo{  
  2.     public void sayHello() {  
  3.         System.out.println("hello world! (version one)");  
  4.     }  

在当前工作目录下建立一个新的目录swap,把编译好的Foo.class文件放在该目录中。接下来要使用我们前面编写的HotswapCL来实现该类的热替换。具体的做法为:我们编写一个定时器任务,每隔2秒钟执行一次。其中,我们会创建新的类加载器实例加载Foo类,生成实例,并调用sayHello方法。接下来,我们会修改Foo类中sayHello方法的打印内容,重新编译,并在系统运行的情况下替换掉原来的Foo.class,我们会看到系统会打印出更改后的内容。定时任务的实现如下(其它代码省略,请读者自行补齐):

 
 
  1. public void run(){  
  2.     try {  
  3.         // 每次都创建出一个新的类加载器 
  4.         HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});  
  5.         Class clcls = cl.loadClass("Foo");  
  6.         Object foo = cls.newInstance();  
  7.  
  8.         Method m = foo.getClass().getMethod("sayHello", new Class[]{});  
  9.         m.invoke(foo, new Object[]{});  
  10.      
  11.     }  catch(Exception ex) {  
  12.         ex.printStackTrace();  
  13.     }  

编译、运行我们的系统,会出现如下的打印:

热替换前的运行结果

图3.热替换前的运行结果

好,现在我们把Foo类的sayHello方法更改为:

 
 
  1. public void sayHello() {  
  2.     System.out.println("hello world! (version two)");  

在系统仍在运行的情况下,编译,并替换掉swap目录下原来的Foo.class文件,我们再看看屏幕的打印,奇妙的事情发生了,新更改的类在线即时生效了,我们已经实现了Foo类的热替换。屏幕打印如下:

热替换后的运行结果

图4.热替换后的运行结果

敏锐的读者可能会问,为何不用把foo转型为Foo,直接调用其sayHello方法呢?这样不是更清晰明了吗?下面我们来解释一下原因,并给出一种更好的方法。如果我们采用转型的方法,代码会变成这样:Foofoo=(Foo)cls.newInstance();读者如果跟随本文进行试验的话,会发现这句话会抛出ClassCastException异常,为什么吗?因为在Java中,即使是同一个类文件,如果是由不同的类加载器实例加载的,那么它们的类型是不相同的。在上面的例子中cls是由HowswapCL加载的,而foo变量类型声名和转型里的Foo类却是由run方法所属的类的加载器(默认为AppClassLoader)加载的,因此是完全不同的类型,所以会抛出转型异常。

那么通过接口调用是不是就行了呢?我们可以定义一个IFoo接口,其中声名sayHello方法,Foo实现该接口。也就是这样:IFoofoo=(IFoo)cls.newInstance();本来该方法也会有同样的问题的,因为外部声名和转型部分的IFoo是由run方法所属的类加载器加载的,而Foo类定义中implementsIFoo中的IFoo是由HotswapCL加载的,因此属于不同的类型转型还是会抛出异常的,但是由于我们在实例化HotswapCL时是这样的:

 
 
  1. HowswapCLcl=newHowswapCL("../swap",newString[]{"Foo"});

其中仅仅指定Foo类由HotswapCL加载,而其实现的IFoo接口文件会委托给系统类加载器加载,因此转型成功,采用接口调用的代码如下:

 
 
  1. public void run(){  
  2.     try {  
  3.         HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});  
  4.         Class clcls = cl.loadClass("Foo");  
  5.         IFoo foo = (IFoo)cls.newInstance();  
  6.         foo.sayHello();  
  7.     } catch(Exception ex) {  
  8.         ex.printStackTrace();  
  9.     }  

确实,简洁明了了很多,在我们的实验中,每当定时器调度到run方法时,我们都会创建一个新的HotswapCL实例,在产品代码中,无需如此,仅当需要升级替换时才去创建一个新的类加载器实例。

转载于:https://www.cnblogs.com/Robin2005/archive/2010/03/04/1678203.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值