0

简而言之,我正在开发一个系统,该系统能够为您提供有关执行 java 程序所提供的结果的信息。考虑过下面的问题,不知道能不能用java解决。

我有以下课程:

public class ClassA {
    ClassB classB= new ClassB();
    public Integer method1(){
       return classB.method2();
    }
}

public class ClassB {
    ClassC classC = new ClassC();
    public Integer method2() {
        return this.classC.method3() + this.classC.method4();
    }
}

public class ClassC {
    public Integer method3() {
        return 3;
    }
    public Integer method4() {
        return 4;
    }
}

到目前为止,我可以通过使用动态代理来捕获方法的每次调用。特别是,我使用包 java.lang.reflect 中的代理和 InvocationHandler 对象。这是我遵循的示例(https://www.concretepage.com/java/dynamic-proxy-with-proxy-and-invocationhandler-in-java)。

我的问题是,如果有人知道我如何提供诸如:“method1() 的返回是从 method2() 的返回生成的,而 method2() 的返回又是从 method3() 的返回生成的, method4() 的返回”。

4

3 回答 3

0

我以前使用过仪器来解决类似的问题。

免责声明:只有在您可以控制 JVM 并且可以指定它使用 javaagent 运行时,您才能执行以下操作。

代理的实现相当简单,您只需要一个实现带有premain(String, java.lang.Instrumentation)签名的方法的类。一个例子如下:

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrumentation.ClassFileTransformer;
import java.lang.instrumentation.IllegalClassFormatException;
import java.lang.instrumentation.Instrumentation;
import java.security.ProtectionDomain;

public class MyAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new ClassTransformer() {
            public byte[] transform(ClassLoader loader, String className,
                    Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                    byte[] classfileBuffer) throws IllegalClassFormatException {
            // if you want to target specific classes or packages you can filter
            // on the class name, just remember that it is the JVM class format string
            if(className.startWith("com/my/package/SomeThing")) {
                // javassist provides methods to access classes and generate bytecode
                ClassPool cp = ClassPool.getDefault();
                // you can access the class with the following
                CtClass cc = cp.get("com.my.package.MyClass$InnerClass");
                // access specific methods with
                CtMethod m = cc.getDeclaredMethod("someMethod");
                m.setBody("{MyCallTree tree = com.my.package.TreeSingleton.getTree();\ntree.addCall(" + m.getLongName + ");\n}");
                return cc.toByteCode();
            }
            else {
                // return null so that you don't break methods or classes you
                // don't want to
                return null;
            }
        });
    }
}

在上面的代码片段中,我将整个方法体替换为作为字符串传递给CtMethod.setBody. 但是,您几乎可以使用 javassist 执行任何操作、前置代码、附加代码等。有关使用 javassist 的详细教程可以在此处找到。这是一个非常强大的库。

您如何实现代码以构建调用信息的细节实际上只是 Java 代码,可能首先将代码编写为项目的一部分,然后只使用在premain方法中执行步骤的位。您甚至可以让它编写tsolakp在他的回答中建议的代码。

下一步是将上述代码打包到一个 jar 文件中,然后在启动时将其注入到您的 JVM 中,如答案中所做的那样。

于 2017-11-09T17:54:55.220 回答
0

您需要使用 ThreadLocals。您的线程本地将有一个 Map 将由每个方法填充。这是一个示例代码:

public static void main(String... args){
        try{
            new ClassA().method1();

            TrackingThreadLocal.tracker.get().entrySet().stream().forEach( (e) -> System.out.println( 
            "the return of " + e.getKey() + " is generated from the return of " + e.getValue().stream().collect( Collectors.joining(", ") ) ) );

        }finally{
            //make sut to clean up to avoid ThreadLocal memoty leak
            TrackingThreadLocal.tracker.remove();
        }
    }

    public class TrackingThreadLocal{

        public static ThreadLocal< Map<String, List<String> > > tracker = new ThreadLocal< Map< String, List<String> > >(){
            @Override 
            public Map< String, List<String> > initialValue() {
                return new HashMap<>();
            }
        };
    }

    public class ClassA {
        ClassB classB= new ClassB();
        public Integer method1(){
           TrackingThreadLocal.tracker.get().put("method1", Arrays.asList("method2") );
           return classB.method2();
        }
    }

    public class ClassB {
        ClassC classC = new ClassC();
        public Integer method2() {
            TrackingThreadLocal.tracker.get().put( "method2",  Arrays.asList("method3", "method4") );
            return this.classC.method3() + this.classC.method4();
        }
    }

    public class ClassC {
        public Integer method3() {
            return 3;
        }
        public Integer method4() {
            return 4;
        }
    }
于 2017-11-09T17:49:47.620 回答
-1

我想到的一件事是检索线程的堆栈跟踪,看看你能做什么。

您可以使用例如。方法并接收StackTraceElementThread.currentThread().getStackTrace()数组,最后使用它们接收带有方法的方法名称。getMethodName()

这只是一个想法,我不确定你是否能得到你想要的信息。

于 2017-11-09T17:12:09.727 回答