java - 使用 Javassist 插入包装原始方法逻辑的 try/finally 逻辑

标签 java bytecode javassist

我有一个 Java 8 项目,它通过自定义 Java 代理使用 Javassist 3.20.0-GA 在运行时重写字节码。目标是检测该方法,使原始主体由带有打印语句的 try/finally block 包装。例如,给定这个简单的方法:

public class TimeService {

    public long getCurrentTime(){

        long time = 0;
        try {
            time = System.currentTimeMillis();
        }
        catch(Throwable t){
            time = -1;
        }

        System.out.println("The current time is "+time);

        return time;
    }
}

我想输出以下修改后的代码(不带注释):

public class TimeService {

    public long getCurrentTime(){
        try {
            System.out.println("Start");

            // BEGIN original code
            long time = 0;
            try {
                time = System.currentTimeMillis();
            }
            catch(Throwable t){
                time = -1;
            }

            System.out.println("The current time is "+time);

            return time;
            // END original code
        }
        finally {
            System.out.println("Stop");
        }
    }
}

首先我尝试了以下代码:

CtClass ctClass =  ClassPool.getDefault().get("com.example.test.TimeService");

CtMethod ctMethod = ctClass.getDeclaredMethod("getCurrentTime");

ctMethod.insertBefore("System.out.println(\"Start\");");
ctMethod.insertAfter("System.out.println(\"Stop\");", true);

这似乎适用于没有预先存在的 try/catch/finally block 的简单 void 方法,但是 getCurrentTime() 方法的输出很奇怪,System.out.println ("Stop") 语句被重复两次:

public long getCurrentTime() {
    boolean var10 = false;

    long var10000;
    long var5;
    try {
        var10 = true;
        System.out.println("Start");
        long time = 0L;

        try {
            time = System.currentTimeMillis();
        } catch (Throwable var11) {
            time = -1L;
        }

        System.out.println("The current time is " + time);
        var10000 = time;
        var10 = false;
    } finally {
        if(var10) {
            var5 = 0L;
            System.out.println("Stop");
        }
    }

    var5 = var10000;
    System.out.println("Stop");
    return var5;
}

上面的代码在没有错误时“有效”,但假装 System.currentTimeMillis() 抛出异常,然后 var10 永远不会设置为 < em>false,因此 System.out.println("Stop") 语句将被执行两次。使用 boolean 标志作为保护措施在这里效果不佳,因此我更愿意在方法的开头和结尾简单地插入一个 try/finally。

接下来我尝试像这样直接检测和替换方法:

CtClass ctClass = ClassPool.getDefault().get("com.example.test.TimeService");

CtMethod ctMethod = ctClass.getDeclaredMethod("getCurrentTime");

ctMethod.instrument(new ExprEditor(){
    public void edit(MethodCall m) throws CannotCompileException {
        String start = "{ System.out.println(\"Start\"); }";
        String stop = "{ System.out.println(\"Stop\"); }";

        m.replace("{ try {"+start+" $_ = $proceed($$); } finally { "+stop+" } }");
    }
});

但这变成了乱码,在许多 try/catch/finally block 中多次重复打印输出 “Start”“Stop”(太乱了,无法粘贴在这里)。

我不确定接下来要尝试什么,或者我是否使用了错误的 Javassist API 部分。看起来它应该非常简单——确实如此,使用简单的 void 方法——但是当返回值时,尤其是与预先存在的 try/catch/finally block 结合使用时,输出变得不可预测。

有什么想法或解决方法吗?

提前致谢。

最佳答案

生成的 getCurrentTime 是正确的。只有在抛出异常时才输入 if(var10) 语句。但在这种情况下,您永远不会到达 finally 语句下面的代码。 finally 不捕获异常,它只是执行一些代码并进一步抛出

生成的代码很奇怪,但它最终完成了它应该做的事情。消息“停止”将只打印一次。

关于java - 使用 Javassist 插入包装原始方法逻辑的 try/finally 逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41156596/

相关文章:

java - 未在字节码中扩展 java.lang.Object。那么为什么编译器不将它添加到新版本的java中呢?

java - 字节码特性在 Java 语言中不可用

java - 将 JVM 字节码往返于文本表示并返回的故障安全方式

java - 使用camel,如何使用 header 的值来定义 XPath 表达式?

java - Java 中有效且漂亮的字符串条件

java - 使用 JsonHttpResponseHandler() 时如何修复 404 错误?

java - 使用 Javassist 获取类中声明字段的索引以在字节码中使用

java - AWS Java 客户端不验证 Java 7 上的发电机端点

java - 为什么我的 Javassist 类没有在运行时执行?

java - 使用 javaagents 在 Java 中进行基本 block 日志记录