Nic*_*ong 2 java aop jvm cglib jvmti
我试图动态修改类,例如在一行之前调用 sleep() 。我在运行时使用Attach方法将代理附加到 jvm 。然后我从 jvm 得到目标类,并修改它(添加一行调用sleep())。而且我遇到了 redine 类错误。我正在使用JDK1.6。我正在使用 ASM 核心 API 来修改类。错误:
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:124)
Run Code Online (Sandbox Code Playgroud)
ASM 代码有问题吗?实际上我的 ASM 代码完成了它的工作(添加一行来调用sleep())。当前的 jvm 不支持 retransform 类吗?似乎无法执行retransformClasses()。难道retransformClasses()不支持ASM操作(添加一行到一个方法调用sleep())?有任何想法吗?谢谢
编辑: 我想修改的类:
import java.util.concurrent.TimeUnit;
public class Person {
public String name = "abc";
public String address = "xxxxx" ;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void sayHello() throws InterruptedException {
System.out.println("aaaaaaaaaa");
System.out.println("Hello World!");
TimeUnit.SECONDS.sleep(120);
System.out.println("dd");
}
public void sayHello2() {
System.out.println("aaaaaaaaaa1");
System.out.println("Hello World!2");
}
public static void main (String args[]) {
try {
Person p = new Person();
p.sayHello(); // linenumber #9. A line to call Sleep() should be added before #here.
p.sayHello2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的 ASM 代码:
public void visitMethodInsn(int arg0, String arg1, String arg2, String arg3) {
Label la=new Label();
mv.visitLabel(la);
int linenumber=la.getOffset();
if(linenumber==9) {
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/util/concurrent/TimeUnit", "SECONDS", "Ljava/util/concurrent/TimeUnit;");
mv.visitLdcInsn(new Long("5"));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/TimeUnit", "sleep", "(J)V");
super.visitMethodInsn(arg0, arg1, arg2, arg3);
}
}
Run Code Online (Sandbox Code Playgroud)
还没看你的代码,我想我可以提出一些建议。首次加载类时,除了存储类的字节码外,JVM 还具有用于跟踪每个类中的字段类型和方法签名的表。
您看到的错误表明该类已加载,此签名信息已存储,然后您尝试在此之后添加该方法。
如果您改为将代理 jar 放在命令行上,您可以在第一次加载类之前做一些事情。如果你在签名信息被存储之前添加你的方法,你应该很好。
如果您必须在流程启动后连接代理,您可能能够转换该类,但您可能只能在不更改字段集、它们的类型、方法或它们的签名的情况下对其进行转换。换句话说,您可以更改字节码,但您必须不能使先前存储的元信息无效。