7000个引用的Java源代码重构

7000个引用的Java源代码重构,java,ide,automated-refactoring,Java,Ide,Automated Refactoring,我需要更改整个代码库中使用的方法的签名 具体地说,方法void log(String)将接受两个额外的参数(Class c,String methodName),这需要调用方提供,具体取决于调用它的方法。我不能简单地传递null或类似值 为了了解范围,Eclipse找到了该方法的7000个引用,因此如果我更改它,整个项目都将失败。我需要几个星期才能手动修复它 据我所知,Eclipse的Eclipse重构插件不能胜任这项任务,但我真的想让它自动化。 那么,我怎样才能完成这项工作呢?应该不会有任何问

我需要更改整个代码库中使用的方法的签名

具体地说,方法
void log(String)
将接受两个额外的参数(
Class c,String methodName
),这需要调用方提供,具体取决于调用它的方法。我不能简单地传递
null
或类似值

为了了解范围,Eclipse找到了该方法的7000个引用,因此如果我更改它,整个项目都将失败。我需要几个星期才能手动修复它

据我所知,Eclipse的Eclipse重构插件不能胜任这项任务,但我真的想让它自动化。
那么,我怎样才能完成这项工作呢?

应该不会有任何问题

我不是Java专家,但类似的东西可以工作。这不是一个完美的解决方案(甚至可能是一个非常糟糕的解决方案),但它可以让您开始:

使用IntelliJ的重构工具更改方法签名,并为2个新参数指定默认值:

c: self.getClass()
methodName: Thread.currentThread().getStackTrace()[1].getMethodName()

或者更好的是,只需指定null作为默认值。

事实上,您的问题不是使用允许您替换所有出现的

log("some weird message");

因为它很少有机会处理各种情况(例如静态方法)

我倾向于建议你看一看。此工具允许源代码解析和转换,允许您以一种缓慢但受控的操作(显然是基于代码的操作)实现您的操作


<>但是,你可以考虑用一个探索堆栈跟踪来转换你的实际方法来获取信息,甚至更好。在内部使用log4j和显示正确信息的日志格式化程序。

Eclipse可以使用重构->更改方法签名来实现这一点,并为新参数提供默认值


对于class参数,defaultValue应该是this.getClass(),但是您的注释是对的,我不知道如何处理methodname参数

如果需要替换的行属于少数类别,那么您需要的是Perl:

find -name '*.java' | xargs perl -pi -e 's/log\(([^,)]*?)\)/log(\1, "foo", "bar")/g'
我猜,将类名(从文件名派生)作为第二个参数的脚本拼凑起来并不难。将方法名作为第三个参数输入作为练习留给读者。

太好了,我可以复制,只需要编辑一点点:


我认为您需要做的是使用类似的源代码解析器来完成这项工作

对于每个java源文件,将其解析为一个编译单元,可能用作基类,并重写(至少)。然后将更改后的CompliationUnit写入一个新文件,然后进行差异处理


我建议不要更改原始源文件,但创建一个影子文件树可能是个好主意(例如,旧文件:
src/main/java/com/mycompany/MyClass.java
,新文件
src/main/refactured/com/mycompany/MyClass.java
,这样可以区分整个目录).

我认为有几个步骤可以解决这个问题,因为这不仅仅是一个技术问题,而是一个“情况”:

  • 由于存在风险,拒绝在短期内完成
  • 指出不使用标准框架而是重新发明轮子所导致的问题(正如Paul所说)
  • 如果进行更改,请坚持使用Log4j或同等产品
  • 在合理的块中使用Eclipse重构来进行更改并处理不同的默认值

  • 我已经在相当大的更改上使用Eclipse重构来修复旧的臭味代码-现在它相当健壮。

    我将搜索
    log(
    )并将其替换为
    log(@class,@methodname,

    然后用任何语言(甚至java)编写一个小脚本来查找类名和方法名,并替换@class和@method标记


    祝你好运

    也许我太天真了,但是为什么你不能重载方法名呢

    void thing(paramA) {
        thing(paramA, THE_DEFAULT_B, THE_DEFAULT_C)
    }
    
    void thing(paramA, paramB, paramC) {
        // new 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
    }
    

    尝试使用intellij进行重构。它有一个称为SSR(结构化搜索和替换)的功能。你可以引用类、方法名等作为上下文。(seanizer的答案更有希望,我投了更高的票)您真的需要更改调用代码和方法签名吗?我的意思是,添加的参数似乎是为了给您提供要添加到日志数据中的调用类和方法。如果唯一的要求只是将调用类/方法添加到日志数据中,那么Thread.currentThread().getStackTrace()应该可以。一旦你有了StackTraceeElement[],你就可以得到调用者的类名和方法名。

    我同意Seanizer的回答,你需要一个能够解析Java的工具。这是必要的,但还不够;你真正想要的是一个能够进行可靠的大规模更改的工具

    要做到这一点,您需要一个能够解析Java、能够与解析代码进行模式匹配、安装替换调用并在不破坏其余源代码的情况下给出答案的工具

    我们可以为各种语言(包括Java)完成所有这些工作。它解析完整的Java源代码系统,构建抽象语法树(用于整个代码集)

    DMS可以应用模式导向的源到源转换来实现所需的更改

    为了达到OP的效果,他将采用以下方法:

    这个规则的意思是,找到一个具有单个字符串参数的对log的调用,并用另两个由辅助函数classmethod确定的参数来替换它

    这些函数确定规则在其中查找的AST节点根的包含方法名和包含类名
    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
    }
    
     rule replace_legacy_log(s:STRING): expression -> expression
        " log(\s) " -> " log( \s, \class\(\), \method\(\) ) "
    
    ... if IsDesiredLog().