动态更改JVM里的class

本文介绍了如何利用agent、Instrumentation及ASM动态地修改正在运行的JVM中的Class文件。主要内容包括动态attach agent到JVM进程的方法、通过Instrumentation API重新加载已加载的类以及使用ASM简化Class文件的修改过程。

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

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

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

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

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

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

[java] view plain copy
  1. publicstaticvoidattach(Stringpid)throwsException{
  2. try{
  3. StringagentPath="/cutemock-agent.jar";
  4. Stringtmp=Main.class.getClassLoader().getResource("com/taobao/lp/cutemock/agent/Main.class").toString();
  5. tmp=tmp.substring(0,tmp.indexOf("!"));
  6. tmp=tmp.substring("jar:".length(),tmp.lastIndexOf("/"));
  7. agentPath=tmp+agentPath;
  8. agentPath=newFile(newURI(agentPath)).getAbsolutePath();
  9. VirtualMachinevm=null;
  10. if(debug){
  11. debugPrint("attachingto"+pid);
  12. }
  13. vm=VirtualMachine.attach(pid);
  14. if(debug){
  15. debugPrint("attachedto"+pid);
  16. }
  17. if(debug){
  18. debugPrint("loading"+agentPath);
  19. }
  20. StringagentArgs="port="+port;
  21. if(debug){
  22. agentArgs+=",debug=true";
  23. }
  24. if(debug){
  25. debugPrint("agentargs:"+agentArgs);
  26. }
  27. vm.loadAgent(agentPath,agentArgs);
  28. if(debug){
  29. debugPrint("loaded"+agentPath);
  30. }
  31. }catch(RuntimeExceptionre){
  32. throwre;
  33. }catch(IOExceptionioexp){
  34. throwioexp;
  35. }catch(Exceptionexp){
  36. throwexp;
  37. }
  38. }

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

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

见如下代码:

[java] view plain copy
  1. Class[]classes=inst.getAllLoadedClasses();
  2. for(Classclazz:classes){
  3. if(clazz.getName().equals(CLASS_NAME)){
  4. System.out.println("addtransformertoTBRemotingRPCProtocolComponent.class");
  5. inst.addTransformer(newMyClassFileTransformer(),true);
  6. inst.retransformClasses(clazz);
  7. }
  8. }

关键在于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:

[java] view plain copy
  1. mv.visitVarInsn(Opcodes.ALOAD,2);
  2. mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"com/taobao/hsf/model/metadata/ServiceMetadata","getUniqueName","()Ljava/lang/String;");
  3. mv.visitVarInsn(Opcodes.ALOAD,1);
  4. mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"com/taobao/hsf/domain/HSFRequest","getMethodName","()Ljava/lang/String;");
  5. mv.visitVarInsn(Opcodes.ALOAD,3);
  6. mv.visitMethodInsn(Opcodes.INVOKESTATIC,"com/taobao/lp/cutemock/agent/MockUtil","getTargetUrl","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
  7. mv.visitVarInsn(Opcodes.ASTORE,3);
  8. Labell4=newLabel();
  9. mv.visitLabel(l4);

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值