Javassist:带有调试信息的复制方法?

时间:2016-06-06 13:19:21

标签: java javassist

在从共同祖先类派生的大量类中,我有方法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 个答案:

答案 0 :(得分:0)

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

顺便说一下,我有同样的问题,我的目标是调试方法roomForSomething。我的解决方案是这样的:首先在类中声明要调试的方法(在你的情况下是roomForSomething())并使其成为静态。

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

在您可以向Javassist注入此修改方法之后调用此静态方法。即使在课堂上没有导入你定义了" roomForSomething"你可以在jsut中指定类的完全限定名,就像我在示例中所做的那样。

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

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