有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaJavassist:copy方法和调试信息?

在从公共祖先类派生的大量类中,我有一个方法something(),我想修改它的行为来度量执行时间。 我在公共祖先类中定义了另外两个方法

// in the common ancestor class:
// this method is defined in all derived classes
public void something() {
    System.out.println("something{");
    System.out.println("something}");
}
// the body of this method will replace the body of something()
public void measuredSomething() {
    System.out.println("measuredSomething{");
    roomForSomething();
    System.out.println("measuredSomething}");

}
// the body of this method will be replaced by the body of something()
public void roomForSomething() {
    // this code will be replaced with something else
    System.out.println("roomForSomething{");
    System.out.println("roomForSomething}");
}

我用Javassist进行替换:

// someClass is known
String superClassName = someClass.getName();
String className = superClassName + "__proxy";
CtClass ctSuperClass = ClassPool.getDefault().get(superClassName);
CtClass ctClass = ClassPool.getDefault().makeClass(className);
ctClass.setSuperclass(ctSuperClass);

// roomForSomething := something
CtMethod methodSomething = ctSuperClass.getMethod("something", "()V");
CtMethod methodSomething2 = CtNewMethod.copy(methodSomething, "roomForSomething", ctClass, null);

ctClass.addMethod(methodSomething2);

// something := measuredSomething
// (and measuredSomething() will call roomForSomething())
CtMethod methodMeasuredSomething = ctSuperClass.getMethod("measuredSomething", "()V");
CtMethod methodMeasuredSomething2 = CtNewMethod.copy(methodMeasuredSomething, "something", ctClass, null);

ctClass.addMethod(methodMeasuredSomething2);

someClass = ctClass.toClass();
result = someClass.newInstance();

它是有效的。但调试器无法显示执行的代码

如何将调试信息与字节码一起复制,以便调试器显示执行的源代码

(修改方法代码时不可能,但在这种特殊情况下应该可以。)

((我确实建议将方法something()分为两个方法,就像有Thread.run()要重写,有Thread.start()要调用一样。我被要求避免这样的更改。)

更新

下面的方法给出了更好的结果:调试器仅在从被调用函数返回时才在通过CtMethod.make()生成的函数中停止;生成的函数在堆栈跟踪中可见;生成的函数的源不可用

// something := measuredSomething
CtMethod methodSomething = CtMethod.make("public void something() {super.measuredSomething();}", ctClass);
ctClass.addMethod(methodSomething);

// roomForSomething := something
CtMethod methodRoomForSomething = CtMethod.make("public void roomForSomething() {super.something();}", ctClass);
ctClass.addMethod(methodRoomForSomething);

如果能够显示生成函数的源代码,那就太好了


共 (1) 个答案

  1. # 1 楼答案

    我不认为您可以使用Javassist直接调试注入的代码,因为这个库只能修改字节码(因此实际上源代码没有更改)。这意味着您的调试器无法访问您注入的代码,因为它基本上不存在(这只是您表达字节码生成的一种图形方式)

    顺便说一下,我也遇到了同样的问题,我的目标是调试roomForSomething方法。我的解决方案是:首先在类中声明要调试的方法(在您的例子中是roomForSomething()),并使其成为静态的

    package p.a.c.k.a.g.e;
    public class ClassName{
        public static void roomForSomething(){
            // here you can debug
        }
    }
    

    在您可以使用Javassist将对该静态方法的调用注入到修改后的方法中之后。即使在类中没有导入定义“roomForSomething”的类,也可以像我在示例中所做的那样指定类的完全限定名

    // roomForSomething := something
    CtMethod methodRoomForSomething = CtMethod.make("public void randomName() {p.a.c.k.a.g.e.ClassName.roomForSomething()}", ctClass);
    ctClass.addMethod(methodRoomForSomething);
    

    使用此解决方案,您可以在“roomForSomething”代码中设置断点