Java源重构7000个引用
我需要更改整个代码库中使用的方法的签名。
具体来说,方法void log(String)
将需要两个额外的参数( Class c, String methodName
),这些参数需要由调用者提供,具体取决于调用它的方法。 我不能简单地传递null
或类似的东西。
为了了解范围,Eclipse发现了7000个对该方法的引用,所以如果我改变它,整个项目就会失效。 我需要几周时间才能手动修复它。
据我所知,Eclipse的Eclipse重构插件不能完成任务,但我真的想让它自动化。
那么,我怎样才能完成工作?
好的,我可以复制我以前的答案,我只需要编辑一点点:
我认为你需要做的是使用像javaparser这样的源代码解析器来做到这一点。
对于每个java源文件,将其解析为CompilationUnit, 创建一个Visitor ,可能使用ModifierVisitorAdapter作为基类,并覆盖(至少) visit(MethodCallExpr, arg)
。 然后将更改的CompilationUnit写入新文件并在之后执行diff。
我建议不要更改原始源文件,但创建一个影子文件树可能是个好主意(例如旧文件: src/main/java/com/mycompany/MyClass.java
,新文件src/main/refactored/com/mycompany/MyClass.java
,这样你可以区分整个目录)。
Eclipse可以使用Refactor – > Change Method签名来执行此操作,并为新参数提供默认值。
对于类参数,defaultValue应该是this.getClass(),但是你的注释正好我不知道如何处理方法名参数。
IntelliJ IDEA应该没有任何问题。
我不是Java专家,但这样的东西可以工作。 这不是一个完美的解决方案(它甚至可能是一个非常糟糕的解决方案),但它可以让你开始:
使用IntelliJ的重构工具更改方法签名,并为2个新参数指定默认值:
c: self.getClass() methodName: Thread.currentThread().getStackTrace()[1].getMethodName()
或者更好的是,只需将null指定为默认值即可。
我认为有几个步骤可以解决这个问题,因为它不仅仅是一个技术问题,而是一个“情况”:
- 由于风险,拒绝在短期内完成。
- 指出不使用标准框架但重新发明轮子所引起的问题(正如Paul所说)。
- 如果进行更改,请坚持使用Log4j或等效项。
- 在合理的块中使用Eclipse重构来进行更改并处理不同的默认值。
我已经使用Eclipse重构来修复旧的臭代码 – 现在它相当强大。
也许我天真,但为什么你不能只重载方法名称?
void thing(paramA) { thing(paramA, THE_DEFAULT_B, THE_DEFAULT_C) } void thing(paramA, paramB, paramC) { // new method }
你真的需要改变调用代码和方法签名吗? 我得到的是,添加的参数看起来像是为了给你提供添加到日志数据的调用类和方法。 如果唯一的要求只是将调用类/方法添加到日志数据中,那么Thread.currentThread()。getStackTrace()应该可以工作。 获得StackTraceElement []后,您可以获取调用者的类名和方法名。
如果您需要替换的行属于少数类别,那么您需要的是Perl:
find -name '*.java' | xargs perl -pi -e 's/log\(([^,)]*?)\)/log(\1, "foo", "bar")/g'
我猜,将一个将classname(从文件名派生)作为第二个参数放入脚本并不太难。 将方法名称作为第三个参数保留为读者的练习。
尝试使用intellij重构。 它有一个名为SSR(结构搜索和替换)的function。 您可以为上下文引用类,方法名称等。 (seanizer的答案更有希望,我赞成它)
我同意Seanizer的回答,你想要一个可以解析Java的工具。 这是必要但不充分的; 你真正想要的是一个可以进行可靠的质量变化的工具。
要做到这一点,您需要一个可以解析Java的工具,可以对解析的代码进行模式匹配,安装替换调用,并在不破坏其余源代码的情况下吐出答案。
我们的DMS软件再造工具包可以为包括Java在内的各种语言完成所有这些工作。 它解析完整的 java系统源代码,构建抽象语法树(用于整个代码集)。
DMS可以应用模式导向的源到源转换来实现所需的更改。
为了达到OP的效果,他将应用以下程序转换 :
rule replace_legacy_log(s:STRING): expression -> expression " log(\s) " -> " log( \s, \class\(\), \method\(\) ) "
这个规则所说的是, 找到一个对具有单个字符串参数的log的调用,并用对辅助函数 类和方法 确定的另外两个参数的log调用替换它 。
这些函数确定包含方法名称并包含规则找到匹配项的AST节点根的类名。
该规则以“源格式”编写,但实际上与AST匹配,并用修改后的AST替换找到的AST。
要获取修改后的源代码,请让DMS简单地进行漂亮打印(制作漂亮的布局)或保真打印(如果您希望保留旧代码的布局)。 DMS保留注释,数字基数等。
如果exisitng应用程序具有多个“log”函数的定义,则需要添加限定符:
... if IsDesiredLog().
其中IsDesiredLog使用DMS的符号表和inheritance信息来确定特定日志是否引用了感兴趣的定义。
事实上你的问题不是使用click’n’play引擎,它可以让你替换所有出现的
log("some weird message");
通过
log(this.getClass(), new Exception().getStackTrace()[1].getMethodName());
因为几乎没有机会处理各种情况(例如静态方法,例如)。
我倾向于建议你看看勺子 。 此工具允许源代码解析和转换,允许您以基于代码的方式实现操作 – 缓慢但受控制的操作。
但是,您可以考虑使用一个探索堆栈跟踪来转换实际方法以获取信息,或者更好的是,在内部使用log4j和显示正确信息的日志格式化程序。
我会搜索并替换log(
用log(@class, @methodname,
然后用任何语言(甚至java)编写一个小脚本来查找类名和方法名,并替换@class和@method标记……
祝好运
如果“这个日志来自哪里?”需要类和方法名称? 键入数据,然后另一个选项是在日志方法中打印出堆栈跟踪。 例如
public void log(String text) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); new Throwable.printStackTrace(pw); pw.flush(); sw.flush(); String stackTraceAsLog = sw.toString(); //do something with text and stackTraceAsLog }