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

时间:2016-12-15 04:46:09

标签: java bytecode javassist

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

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块的简单void方法,但 getCurrentTime()方法的输出很奇怪,并且 System.out.println (“停止”)语句重复两次

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”)语句将执行两次。使用布尔标志作为安全措施在这里不能很好地工作,因此我宁愿在方法的开头和结尾插入一个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块中多次重复“开始”“停止”打印输出(太乱了,无法粘贴)这里)。

我不确定接下来要尝试什么,或者我使用的是Javassist API的错误部分。看起来它应该非常简单 - 而且,使用简单的void方法 - 但是当返回值时,特别是与预先存在的try / catch / finally块组合时,输出变得不可预测。

有任何想法或解决方法吗?

提前致谢。

1 个答案:

答案 0 :(得分:2)

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

生成的代码非常奇怪,但它最终会完成它应该做的事情。消息&#34;停止&#34;将打印一次。