自动生成Java源代码

7
我正在寻找一种方法,可以根据类中定义的字段,在现有Java源代码文件中自动生成新方法的源代码。
实际上,我正在寻找执行以下步骤的方法:
  1. 读取和解析SomeClass.java
  2. 遍历源代码中定义的所有字段
  3. 添加源代码方法someMethod()
  4. 保存SomeClass.java(最好保留现有代码的格式)
哪些工具和技术最适合完成这项任务?
编辑
我不想在运行时生成代码;我想增强现有的Java源代码。

另外,您可以编写更好的接口。代码生成很少需要,因为大多数情况下它可以被更好的接口替代。例如,您可以让 SomeClass.java 实现一个提供 someMethod() 的接口,或者您可以让它扩展一个抽象类,该抽象类为您提供所需的 someMethod() 实现。如果 someMethod() 在任何地方都是相同的,您还可以添加一个单独的接口和实现,以便在任何想要使用它的地方启用其使用。 - ThePyroEagle
7个回答

12
你需要的是一个程序转换系统。
好的系统会针对你所关心的语言提供解析器,构建表示已解析代码的AST,为你提供访问AST以进行分析和修改,并可以从AST中重新生成源代码。你提到的“扫描字段”只是遍历表示程序的AST的一种方式。对于你产生的每个有趣的分析结果,你都希望对AST进行更改,可能是在其他地方,但仍然在AST中。在所有更改完成后,你希望以注释的形式重新生成文本(与最初输入的相同或与你在新代码中构造的相同)。
有几个工具专门针对Java执行此操作。 Jackpot 提供解析器,构建AST,并允许你编写Java过程来处理树。优点:概念易懂。缺点:你需要编写更多的Java代码来遍历/修改树。 Jackpot仅适用于Java。

StrategoTXL可以解析您的代码,构建AST,并让您编写“源到源”转换(使用目标语言的语法,例如此处的Java)来表达模式和修复。额外的好消息是:您可以定义任何您喜欢的编程语言作为要处理的目标语言,这两者都有Java定义。但是它们在分析方面比较薄弱:通常您需要符号表和数据流分析才能真正进行所需的分析和更改。而且它们坚持认为一切都是重写规则,无论是否有帮助;这有点像坚持认为您只需要锤子就可以完成所有工具箱中的工作;毕竟,每件事都可以像钉子一样处理,对吗?

我们的DMS软件重构工具包允许定义任意目标语言(并具有许多预定义的语言,包括Java),包括Stratego、TXL的所有源到源转换功能,以及Jackpot的过程能力,并提供符号表、控制和数据流分析信息。编译器专家告诉我们,这些东西对于构建强大的编译器(=“分析+优化+细化”)是必要的,对于代码生成系统也是如此,原因完全相同。使用这种方法,您可以生成代码并对其进行优化,以达到您所了解的程度。一个例子,类似于您的序列化想法,是为指定的XML DTD生成快速的XML读取器和写入器;我们已经在Java和COBOL的DMS中完成了这项工作。
DMS已被用于读取/修改/写入许多种类的源文件。一个很好的例子可以在这篇技术论文中找到,它展示了如何修改代码以插入仪器探针:分支覆盖变得容易。一个更简单但更完整的例子是在如何转换代数中找到的,使用相同的思想来定义任意语言和应用转换。

4
请查看Java Emitter Templates。它们允许您使用标记语言创建Java源代码文件。这类似于使用脚本语言生成HTML,只是您生成的是可编译的源代码。 JET的语法与JSP非常相似,因此不太难学习。但是,对于您想要实现的目标来说,这可能有些过头了。如果您决定走这条路,以下是一些资源:

3

你可以使用 cglib 在运行时生成代码。


3
使用自动生成的代码修改相同的Java源文件是维护的噩梦。考虑生成一个新类,它继承您当前的类并添加所需的方法。使用反射从用户定义的类中读取,并为自动生成的类创建模板。然后针对每个用户定义的类生成其扩展类。将代码生成阶段集成到您的构建生命周期中。
或者您可以使用“字节码增强”技术来增强类,而无需修改源代码。
更新:
1. 混合自动生成的代码总是存在风险,因为有人可能会在未来修改它以调整小行为。这只是下一次构建的问题,当这些更改将被丢失时。
2. 您将不得不完全依赖于自动生成的源代码顶部的注释,以防止开发人员这样做。
3. 版本控制 - 假设您更新了某个方法的模板,现在所有源文件的版本都将更新,即使源更新是自动生成的。您将看到冗余历史记录。

2
谢谢。我的想法是注入自动生成的代码比字节码增强的“隐形魔法”更清晰、更易于维护,但我很想了解关于我提出的方法可维护性的具体细节。 - Tony the Pony
我已经将它作为一个新问题发布了:http://stackoverflow.com/questions/5746099/automatic-source-code-generation-good-idea-or-potential-nightmare - Tony the Pony

1

遍历字段并定义someMethod是一个相当模糊的问题陈述,因此很难给出非常有用的答案,但Eclipse的重构支持提供了一些优秀的工具。它将为您提供初始化所选定义成员集的构造函数,并为您定义toString方法。

我不知道您想考虑哪些其他someMethod(),但这是一个开始。


1
我非常警惕将生成的代码注入到包含手写代码的文件中。手写代码应该被检入版本控制,但生成的代码不应该;代码生成应该作为构建过程的一部分完成。您必须构建您的构建过程,以便为每个文件创建一个临时副本,将生成的源代码注入其中,并编译结果,而不触及开发人员工作的原始源文件。

谢谢!我正在寻找为模型类生成持久化代码的方法,这些代码只会在类代码本身发生更改时才会更改。(不幸的是,Java中没有“partial”类) - Tony the Pony
@Jen,使用反射来完成这个任务是否更有意义,可能需要在模型类上使用注释进行指导,而不是实际修改每个需要持久化的类? - Wyzard
1
是的,那也是一种有效的方法,但我正在寻找 a) 更高的性能和 b) 更大的代码清晰度(我的想法是通过阅读生成的代码比尝试解决不同的约定/注释更容易了解正在发生的事情)。 - Tony the Pony

1

Antlr 是一个非常棒的工具,可以轻松地将 Java 源代码转换为 Java 源代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接