关于修改class后reload需要解决的几个问题

我们要解决的问题就是当一个类(例如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这样的类了?显然不行。

猜你喜欢

转载自summer85.iteye.com/blog/2202580
今日推荐