如何在Java中跟踪方法结果
简单地说,我正在开发一个系统,它能够为您提供有关java程序执行结果的信息。我已经考虑了以下问题,我不知道是否可以用java解决它 我有以下课程:如何在Java中跟踪方法结果,java,reflection,proxy-pattern,Java,Reflection,Proxy Pattern,简单地说,我正在开发一个系统,它能够为您提供有关java程序执行结果的信息。我已经考虑了以下问题,我不知道是否可以用java解决它 我有以下课程: public class ClassA { ClassB classB= new ClassB(); public Integer method1(){ return classB.method2(); } } public class ClassB { ClassC classC = new Clas
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对象。下面是我遵循的示例()
我的问题是,如果有人知道我如何提供以下信息:
“method1()的返回由method2()的返回生成,method2()的返回由method3()的返回和method4()的返回依次生成。” 我想到的一件事是检索线程的堆栈跟踪,看看你能用它做些什么 您可以使用例如
Thread.currentThread().getStackTrace()
方法来执行此操作,并接收一个数组,最后使用getMethodName()
方法来接收方法名
这只是一个想法,我不确定你是否能得到你想要的信息。你需要利用ThreadLocals。您的线程本地将有一个映射,该映射将由每个方法填充。下面是一个示例代码:
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;
}
}
publicstaticvoidmain(字符串…参数){
试一试{
新的ClassA().method1();
TrackingThreadLocal.tracker.get().entrySet().stream().forEach((e)->System.out.println(
“+e.getKey()+”的返回是从“+e.getValue().stream().collect(Collectors.joining(“,”)的返回中生成的);
}最后{
//将sut清理干净,避免螺纹局部漏丝
TrackingThreadLocal.tracker.remove();
}
}
公共类跟踪线程本地{
public static ThreadLocal
我以前使用过仪器来解决类似的问题
免责声明:只有在控制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;
}
});
}
}
导入javassist.ClassPool;
导入javassist.CtClass;
导入javassist.CtMethod;
导入java.lang.instrumentation.ClassFileTransformer;
导入java.lang.instrumentation.IllegalClassFormatException;
导入java.lang.instrumentation.instrumentation;
导入java.security.ProtectionDomain;
公共类MyAgent{
公共静态无效预输入(字符串代理、仪器仪表){
inst.addTransformer(新类Transformer(){
公共字节[]转换(类加载器、字符串类名称、,
类ClassBeingRefined,ProtectionDomain ProtectionDomain,
字节[]classfileBuffer)引发IllegalClassFormatException{
//如果您想针对特定的类或包,可以进行筛选
//在类名上,只需记住它是JVM类格式字符串
if(className.startWith(“com/my/package/SomeThing”)){
//javassist提供了访问类和生成字节码的方法
ClassPool cp=ClassPool.getDefault();
//您可以使用以下命令访问该类
CtClass cc=cp.get(“com.my.package.MyClass$InnerClass”);
//使用
CTM方法=cc.getDeclaredMethod(“someMethod”);
m、 setBody(“{MyCallTree-tree=com.my.package.TreeSingleton.getTree();\ntree.addCall(“+m.getLongName+”);\n}”);
返回cc.toByteCode();
}
否则{
//返回null,这样您就不会破坏您需要的方法或类
//不想
返回null;
}
});
}
}
在上面的代码片段中,我用作为字符串传递给CtMethod.setBody
的代码替换了整个方法体。但是,您几乎可以使用javassist、前置代码、追加代码等执行任何操作。可以找到有关使用javassist的详细教程。它是一个非常强大的库
您如何实现代码以建立呼叫信息的详细信息实际上只是Java代码,可能先将代码作为项目的一部分编写,然后使用premain
方法中的步法。您甚至可以让它编写他在回答中建议的代码
下一步是将上述代码打包到jar文件中,然后在启动JVM时将其注入JVM,如回答中所述。有一个调试器api和其他相关的低级api。您可以从程序本身运行的运行时内部执行的操作相当有限。另一个选项是,如果您可以控制JVM,是指令插入(
java.lang.instrumentation
)。加载类时,可以插入