我想打印出带有参数名称和值的Java方法调用,并返回结果。
我不想手动添加跟踪语句,特别是当代码在第三方库中时。我需要了解与库的交互,特别是当使用回调时。
我尝试使用包装器,但遇到了问题,所以子类化是更好的。(即wrappedoobject . methoda()或super.methodA()调用)
写这段代码很痛苦,尤其是当有很多方法的时候。
我希望Java可以自动完成这个任务,因为它有一切使这成为可能。
最好的方法是什么?用包装器或子类替换对象是一种折衷。
因此,下一步是将跟踪代码添加到包装器或子类中。我想写一个解析器来生成代码。我已经使用了yacc &我刚发现了Antlr。
Antlr是做这件事的正确工具吗?请问我该怎么做?我以前没有使用过Antlr,但在周围见过。
谢谢。
这是我想做的-
// 3rdParty library under investigation, with evolving versions
import com.3rdParty.lib.Service;
import com.3rdParty.lib.Callback;
MyExistingClass {
Service service = new Service();
// Need to understand 3rd party library service and callback interactions
// Also need to write my own callbacks using 3rd party interface
if (traceMethod1) {
service.doSomething(param1, new CallbackWrapper(), param3);
}
else if (traceMethod2) {
service.doSomething(param1, new CallbackSubclass(), param3);
}
else {
// Original code
// Service calls Callback methods
service.doSomething(param1, new Callback(), param3);
}
}
--------------------------------------------------------------------------------
// 3rd Party code - Service calls Callback methods
package com.3rdParty.lib;
public Callback extends SomeBaseClass {
public void methodA(int code, String action, SomeClass data) {
// do method A stuff
}
public String methodB(String name, boolean flag) {
// do method B stuff
return result;
}
...etc.
}
--------------------------------------------------------------------------------
// Wrapper class - traceMethod1
package com.my.package;
import com.3rdParty.lib.Callback;
public CallbackWrapper implements SomeCallbackInterface {
Callback cb = new Callback();
public void methodA(int code, String action, SomeClass data) {
logger.debug("CallbackWrapper.methodA() called");
logger.debug(" code = " + code);
logger.debug(" action = " + action);
logger.debug(" data = " + data);
cb.methodA(code, action, data);
logger.debug("CallbackWrapper.methodA() returns");
}
public String methodB(String name, boolean flag) {
logger.debug("CallbackWrapper.methodB() called");
logger.debug(" name = " + name);
logger.debug(" flag = " + flag);
String result = cb.methodB(name, flag);
logger.debug("CallbackWrapper.methodB() returns result = " + result);
return result;
}
...etc.
}
--------------------------------------------------------------------------------
// Subclass - traceMethod2
package com.my.package;
import com.3rdParty.lib.Callback;
public CallbackSubclass extends Callback {
public void methodA(int code, String action, SomeClass data) {
logger.debug("CallbackSubclass.methodA() called");
logger.debug(" code = " + code);
logger.debug(" action = " + action);
logger.debug(" data = " + data);
super.methodA(code, action, data);
logger.debug("CallbackSubclass.methodA() returns");
}
public String methodB(String name, boolean flag) {
logger.debug("CallbackSubclass.methodB() called");
logger.debug(" name = " + name);
logger.debug(" flag = " + flag);
String result = super.methodB(name, flag);
logger.debug("CallbackSubclass.methodB() returns result = " + result);
return result;
}
...etc.
}
在Java中做这类事情最简单的方法是使用字节码而不是源代码。使用BCEL (https://commons.apache.org/proper/commons-bcel/)或ASM (http://asm.ow2.org/),您可以动态地创建和加载现有类的修改版本,甚至是从头生成的全新类。
这仍然不容易,但比尝试进行源代码翻译要容易得多。
对于跟踪方法调用的特殊问题,您可以创建一个自定义ClassLoader,它可以用自定义跟踪代码自动检测它加载的每个类中的每个方法。
ANTLR不是合适的工具。虽然您可以获得用于构建ast的ANTLR的Java语法,但ANTLR在修改ast或重新生成可编译的源文本方面不会提供太多帮助。
你需要的是一个程序转换系统(PTS)。这些工具可以解析源代码,构建ast,为您提供通过源到源转换修改这些ast的方法,并且可以从修改过的树中重新生成可编译的源文本。
PTS的源到源转换通常根据要转换的语言语法(在本例中为java)编写: if you see *this*, replace it by *that* if some *condition*
我们的DMS软件再造工具包就是这样一个具有可用Java前端的PTS。
你想做的很像插装代码;只需修改受害者方法,使其执行所需的跟踪即可。请参阅分支覆盖变得简单的示例,了解我们如何使用这种源代码到源代码的重写来实现Java的插装工具。
或者,您可以编写规则,将受害者类和方法复制为子类,并按照您所建议的那样连接跟踪。检测可能比复制所有东西更容易。(当然,当你在编写转换规则时,你真的不关心代码改变了多少,因为在你的情况下,你会在完成它后把它扔掉;你在乎写规则要花多少精力]
参见DMS重写规则,详细讨论了这些规则的真正样子,以及重写规则对源代码进行更改的工作示例。
其他人建议对编译的字节码进行转换。是的,它的工作原理与PTS系统的工作原理相同,但是您需要手工编写代码,并且转换是由一堆在JVM指令上操作的过程性代码编写的,仅凭人类是无法读取的。在没有其他选择的情况下,我理解人们采取这种方法。我的主要观点是是有选择的