`
argan
  • 浏览: 125896 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

实现增强的java class hotswap (三) 解决方案

阅读更多

这段时间瞎忙,这篇文章没有写下去,被人鄙视是太监文,郁闷,只好抽时间继续。

----------------------------

上回说道我们要实现类字节码的动态装载,一个思路就是不让这个类真正的被装载进虚拟机,因为一旦真正的装载进来了,就无法对其进行结构上的修改,这些在前文中已经提到过了。那怎么才能不让类装载进来但是又能使用它呢?

 

这看起来像一个悖论,我们要在应用是使用一个类,初始化它,构造它的实例,调用方法,等等,但是又不能装载这个类,这不忽悠人么?

 

暂时不管如何做到,先看看问题本身。我们要解决的问题就是当一个类(例如Worker)已经在虚拟机里面使用了,比如有程序Test引用到了Worker,这时候,Worker修改过了(Work'),字节码更新过了,我们需要在不重新启动jvm的情况下,让Test新发起的调用都使用到Work'的代码。我们怎么处理呢?

 

我们的方案:虚拟机起来的时候,在Test里,发现(TODO1)要使用Worker类,我们装载一个WorkerVersion1给Test使用,当发现(TODO2)Worker类的字节码变化了,我们需要默默的装载(TODO3)一个WorkerVersion2进来,给Test使用,同时需要不让Test知道有这个变化。

 

这几个TODO解决掉了,我们的问题也差不多了。

 

* TODO1,监控虚拟机装载类

这个不难,我们用agent去enhance一下java.lang.ClassLoader,修改findClass/findResource等方法,加入我们自己的检查和装载逻辑,然后redefine这个类即可,没有问题

 

* TODO2,监控类字节码变化

在装载这个类的时候,记录下这个类的来源,例如来自某个目录,来自某个jar,然后在需要的时候检查这个资源是否改变,如果改变了就发起重新装载的消息即可,也没啥问题

 

* TODO3,重新装载一个变化了的类

这里才是技巧的核心,我们在装载类的时候,使用字节码解析工具(例如asm),直接找到字节码,修改它,生成一个带版本号的类名,例如将Worker修改成Worker_V_1,返回给上面使用,当发现类有修改的时候,我们就装载一个Worker_V_2出来,给上面使用。

 

在我们修改字节码的时候,需要处理很多东西,例如,处理Test类的时候,发现他要调用Worker类的一个go方法,我们需要先检查Worker类的当前版本,然后再去调用找到的当前版本类的相应方法,这个必须是动态的,否则Worker变成V2了,Test还在调用V1上的方法。同样的,出了拦截方法调用(INVOKE_INTERFACE,INVOKE_STATIC,INVOKE_VIRTUAL,INVOKE_SPECIAL,INVOKE_DYNAMIC)以外还要处理field的读写(PUTFEILD,PUTSTATIC,GETFIELD,GETSTATIC),和方法调用的处理类似,需要动态的检查操作的类。

 

但是这样,问题又出来了,如果我使用反射呢?岂不是会得到Worker_V_1这样的类了?显然不行。

 

 

 

2
1
分享到:
评论
2 楼 argan 2009-08-10  
to be continued....hehe
1 楼 axman 2009-08-10  
argan,这个还是太监帖啊

相关推荐

Global site tag (gtag.js) - Google Analytics