有 Java 编程相关的问题?

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

反射如何在Java中跟踪方法结果

简单地说,我正在开发一个系统,它能够为您提供有关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包中的代理和InvocationHandler对象。朗。反思。这里有一个我遵循的例子(https://www.concretepage.com/java/dynamic-proxy-with-proxy-and-invocationhandler-in-java

我的问题是,如果有人知道我如何提供以下信息: “method1()的返回由method2()的返回生成,method2()的返回由method3()的返回和method4()的返回依次生成。”


共 (3) 个答案

  1. # 1 楼答案

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

    您可以使用例如Thread.currentThread().getStackTrace()方法接收StackTraceElement数组,最后使用它们接收getMethodName()方法的方法名

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

  2. # 2 楼答案

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

    Disclaimer: You can only do the following if you have control over the JVM and you can specify it to run with a 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、前置代码、追加代码等几乎可以做任何事情。可以在here中找到有关使用javassist的详细教程。这是一个非常强大的图书馆

    关于如何实现代码来构建调用信息的细节实际上只是Java代码,可能首先将代码作为项目的一部分编写,然后使用premain方法中的一些位来完成后续工作。你甚至可以让它编写tsolakp在他的答案中建议的代码

    下一步是将上述代码打包到一个jar文件中,然后在启动JVM时将其注入JVM,如this应答中所述

  3. # 3 楼答案

    你需要利用当地人。你的线程本地将有一个映射,每个方法都将填充该映射。下面是一个示例代码:

    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;
            }
        }