动态更改JVM里的class

近来需要完成一个feature:修改已load到JVM中的某个class,对其加一些代码,以此来动态修改运行中的程序。

对着这个feature我找到的方案是agent+Instrumentation+ASM

一路做下来有以下几点比较有意思:

1)动态attach agent到某个JVM进程

一般使用agent都是静态的,直接在运行某程序时加agent参数,这样agent会先于程序启动,这个不符合我的需求,我找到一个动态attach agent的方法,具体细节见以下代码:

public static void attach(String pid) throws Exception { try { String agentPath = "/cutemock-agent.jar"; String tmp = Main.class.getClassLoader().getResource("com/taobao/lp/cutemock/agent/Main.class").toString(); tmp = tmp.substring(0, tmp.indexOf("!")); tmp = tmp.substring("jar:".length(), tmp.lastIndexOf("/")); agentPath = tmp + agentPath; agentPath = new File(new URI(agentPath)).getAbsolutePath(); VirtualMachine vm = null; if (debug) { debugPrint("attaching to " + pid); } vm = VirtualMachine.attach(pid); if (debug) { debugPrint("attached to " + pid); } if (debug) { debugPrint("loading " + agentPath); } String agentArgs = "port=" + port; if (debug) { agentArgs += ",debug=true"; } if (debug) { debugPrint("agent args: " + agentArgs); } vm.loadAgent(agentPath, agentArgs); if (debug) { debugPrint("loaded " + agentPath); } } catch (RuntimeException re) { throw re; } catch (IOException ioexp) { throw ioexp; } catch (Exception exp) { throw exp; } }

这段代码的关键是要找到agent的jar包,然后通过VirtualMachine.attach和VirtualMachine.loadAgent把agent attach到pid上

2)通过Instrumentation修改已load了的class

见如下代码:

Class[] classes = inst.getAllLoadedClasses(); for(Class clazz : classes){ if(clazz.getName().equals(CLASS_NAME)){ System.out.println("add transformer to TBRemotingRPCProtocolComponent.class"); inst.addTransformer(new MyClassFileTransformer(),true); inst.retransformClasses(clazz); } }

关键在于inst.addTransformer(new MyClassFileTransformer(),true);这个true参数,inst.retransformClasses(clazz);只会重新修改addTransformer中canRetransform==true的

3)通过asm eclipse plugin方便修改class

大家都知道可以通过asm来修改class,但其api及其难用,比如我仅仅只想加一行:

targetURL = MockUtil.getTargetUrl(metadata.getUniqueName(), request.getMethodName(), targetURL);

翻译为asm:

mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/taobao/hsf/model/metadata/ServiceMetadata", "getUniqueName", "()Ljava/lang/String;"); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/taobao/hsf/domain/HSFRequest", "getMethodName", "()Ljava/lang/String;"); mv.visitVarInsn(Opcodes.ALOAD, 3); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/taobao/lp/cutemock/agent/MockUtil", "getTargetUrl", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); mv.visitVarInsn(Opcodes.ASTORE, 3); Label l4 = new Label(); mv.visitLabel(l4);

但asm提供了一个eclipse plugin,更新地址为:http://andrei.gmxhome.de/eclipse/

它可以对比出修改前后的class的差异,并自动翻译为asm代码

以上是我这两天玩动态修改class的一些心得,有点乱,但确实是不断尝试后的心得

你可能感兴趣的:(技术积累)