【问题标题】:A Java API to generate Java source files [closed]用于生成 Java 源文件的 Java API [关闭]
【发布时间】:2010-09-12 09:19:20
【问题描述】:

我正在寻找一个框架来生成 Java 源文件。

类似于以下 API:

X clazz = Something.createClass("package name", "class name");
clazz.addSuperInterface("interface name");
clazz.addMethod("method name", returnType, argumentTypes, ...);

File targetDir = ...;
clazz.generate(targetDir);

然后,应该在目标目录的子目录中找到一个java源文件。

有人知道这样的框架吗?


编辑

  1. 我真的需要源文件。
  2. 我也想填写方法的代码。
  3. 我正在寻找高级抽象,而不是直接的字节码操作/生成。
  4. 我还需要对象树中的“类结构”。
  5. 问题域很笼统:生成大量非常不同的类,没有“共同结构”。

解决方案
根据您的回答,我发布了 2 个答案...with CodeModelwith Eclipse JDT

我在我的解决方案中使用了CodeModel,:-)

【问题讨论】:

  • 你的问题很笼统,你的问题域真的这么笼统吗?您能否更具体地了解您的问题域?例如,我编写了代码生成工具来为特定问题生成代码,例如消除重复的异常类代码,或消除枚举中的重复。
  • @Vlookward:您可以将放在问题中的答案作为下面的 2 个单独答案移动。然后为每个问题添加一个链接。
  • @Banengusk:感谢您的提问,为我节省了数小时搜索互联网最黑暗部分的时间。 @skaffman:很棒的发现 - 你让另一位开发人员更轻松地完成了他即将完成的任务 :)
  • 这个 SO 答案解决了 C++ 而不是 Java 的问题,但答案也适用于 Java。 stackoverflow.com/a/28103779/120163

标签: java eclipse code-generation


【解决方案1】:

Sun 提供了一个名为 CodeModel 的 API,用于使用 API 生成 Java 源文件。获取信息并不是最容易的事情,但它就在那里,而且效果非常好。

获取它的最简单方法是作为 JAXB 2 RI 的一部分 - XJC schema-to-java 生成器使用 CodeModel 生成其 java 源代码,它是 XJC jar 的一部分。您可以将其仅用于 CodeModel。

http://codemodel.java.net/获取它

【讨论】:

【解决方案2】:

使用 CodeModel 找到解决方案
谢谢skaffman

例如,使用以下代码:

JCodeModel cm = new JCodeModel();
JDefinedClass dc = cm._class("foo.Bar");
JMethod m = dc.method(0, int.class, "foo");
m.body()._return(JExpr.lit(5));

File file = new File("./target/classes");
file.mkdirs();
cm.build(file);

我可以得到这个输出:

package foo;
public class Bar {
    int foo() {
        return  5;
    }
}

【讨论】:

  • 这看起来很棒。你如何生成一个方法来返回另一个使用 CodeModel 生成的类型?
  • @AndrásHummer 使用从cm._class(...) 返回的实例作为dc.method(...) 的返回类型参数。
【解决方案3】:

使用 Eclipse JDT 的 AST 找到解决方案
谢谢Giles

例如,使用以下代码:

AST ast = AST.newAST(AST.JLS3);
CompilationUnit cu = ast.newCompilationUnit();

PackageDeclaration p1 = ast.newPackageDeclaration();
p1.setName(ast.newSimpleName("foo"));
cu.setPackage(p1);

ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "util", "Set" }));
cu.imports().add(id);

TypeDeclaration td = ast.newTypeDeclaration();
td.setName(ast.newSimpleName("Foo"));
TypeParameter tp = ast.newTypeParameter();
tp.setName(ast.newSimpleName("X"));
td.typeParameters().add(tp);
cu.types().add(td);

MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);

Block block = ast.newBlock();
md.setBody(block);

MethodInvocation mi = ast.newMethodInvocation();
mi.setName(ast.newSimpleName("x"));

ExpressionStatement e = ast.newExpressionStatement(mi);
block.statements().add(e);

System.out.println(cu);

我可以得到这个输出:

package foo;
import java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}

【讨论】:

  • 请问 - 您是作为 Java Eclipse 插件的一部分执行此操作,还是设法将其用作独立代码?我意识到这是几年前的事了。
  • @mtrc 如果我没记错的话,它是 Eclipse 中的一个独立且普通的 java 项目,将正确的 jar 添加到类路径中 - 但我不记得文件名了。
【解决方案4】:

您可以使用 Roaster (https://github.com/forge/roaster) 进行代码生成。

这是一个例子:

JavaClassSource source = Roaster.create(JavaClassSource.class);
source.setName("MyClass").setPublic();
source.addMethod().setName("testMethod").setPrivate().setBody("return null;")
           .setReturnType(String.class).addAnnotation(MyAnnotation.class);
System.out.println(source);

将显示以下输出:

public class MyClass {
   private String testMethod() {
       return null;
   }
}

【讨论】:

    【解决方案5】:

    另一种选择是 Eclipse JDT 的 AST,如果您需要重写任意 Java 源代码,而不仅仅是生成源代码,它会非常有用。 (而且我相信它可以独立于 eclipse 使用)。

    【讨论】:

    • 太棒了!!我正在寻找一个抽象语法树...现在我将搜索有关 API 的更多信息...谢谢!,:-)
    • API 很复杂,正如我所料。但它具有我需要的所有功能。谢谢,贾尔斯。
    • 正如@gastaldi 所提到的,roaster(来自 JBoss Forge)是 Eclipse JDT 的一个很好的包装器。它隐藏了 JDT 的复杂性,并提供了一个很好的 API 来解析、修改或编写 java 代码。 github.com/forge/roaster
    【解决方案6】:

    Eclipse JET 项目可用于生成源代码。我不认为它的 API 与您描述的完全一样,但每次我听说有一个项目进行 Java 源代码生成时,他们都会使用 JET 或自制工具。

    【讨论】:

      【解决方案7】:

      不了解库,但您可能只需要一个通用模板引擎。有a bunch of them,我个人对FreeMarker有很好的体验

      【讨论】:

        【解决方案8】:

        我构建了一个看起来非常像您的理论 DSL 的东西,称为“sourcegen”,但从技术上讲,而不是我编写的 ORM 的 util 项目。 DSL 看起来像:

        @Test
        public void testTwoMethods() {
            GClass gc = new GClass("foo.bar.Foo");
        
            GMethod hello = gc.getMethod("hello");
            hello.arguments("String foo");
            hello.setBody("return 'Hi' + foo;");
        
            GMethod goodbye = gc.getMethod("goodbye");
            goodbye.arguments("String foo");
            goodbye.setBody("return 'Bye' + foo;");
        
            Assert.assertEquals(
            Join.lines(new Object[] {
                "package foo.bar;",
                "",
                "public class Foo {",
                "",
                "    public void hello(String foo) {",
                "        return \"Hi\" + foo;",
                "    }",
                "",
                "    public void goodbye(String foo) {",
                "        return \"Bye\" + foo;",
                "    }",
                "",
                "}",
                "" }),
            gc.toCode());
        }
        

        https://github.com/stephenh/joist/blob/master/util/src/test/java/joist/sourcegen/GClassTest.java

        它还做了一些简洁的事情,例如“自动组织导入”参数/返回类型中的任何 FQCN、自动修剪在此代码生成运行中未触及的任何旧文件、正确缩进内部类等。

        这个想法是生成的代码应该看起来很漂亮,没有警告(未使用的导入等),就像您的其他代码一样。这么多生成的代码读起来很难看……太可怕了。

        无论如何,文档并不多,但我认为 API 非常简单/直观。 Maven repo 是here,如果有人感兴趣的话。

        【讨论】:

          【解决方案9】:

          如果您真的需要源,我不知道任何会生成源的东西。但是,您可以使用 ASMCGLIB 直接创建 .class 文件。

          你也许可以从这些生成源代码,但我只用它们来生成字节码。

          【讨论】:

            【解决方案10】:

            我自己做的是一个模拟生成器工具。这是一项非常简单的任务,即使您需要遵循 Sun 格式指南。我敢打赌,你会比在 Internet 上找到符合你目标的代码更快地完成代码。

            您基本上已经自己概述了 API。现在只需用实际代码填充它!

            【讨论】:

            • 嘿嘿嘿...如果没有找到框架那我就写吧。我想要很多功能,所以我不会在早上得到它......
            【解决方案11】:

            还有StringTemplate。由ANTLR作者出品,功能相当强大。

            【讨论】:

              【解决方案12】:

              有新项目write-it-once。基于模板的代码生成器。您使用Groovy 编写自定义模板,并根据java 反射生成文件。这是生成任何文件的最简单方法。可以通过生成AspectJ文件、基于JPA注解的SQL、基于枚举的插入/更新等方式来制作getters/settest/toString。

              模板示例:

              package ${cls.package.name};
              
              public class ${cls.shortName}Builder {
              
                  public static ${cls.name}Builder builder() {
                      return new ${cls.name}Builder();
                  }
              <% for(field in cls.fields) {%>
                  private ${field.type.name} ${field.name};
              <% } %>
              <% for(field in cls.fields) {%>
                  public ${cls.name}Builder ${field.name}(${field.type.name} ${field.name}) {
                      this.${field.name} = ${field.name};
                      return this;
                  }
              <% } %>
                  public ${cls.name} build() {
                      final ${cls.name} data = new ${cls.name}();
              <% for(field in cls.fields) {%>
                      data.${field.setter.name}(this.${field.name});
              <% } %>
                      return data;
                  }
              }
              

              【讨论】:

                【解决方案13】:

                这真的取决于你想要做什么。代码生成本身就是一个主题。如果没有特定的用例,我建议查看速度代码生成/模板库。另外,如果您要离线进行代码生成,我建议您使用 ArgoUML 之类的工具从 UML 图/对象模型转换为 Java 代码。

                【讨论】:

                  【解决方案14】:

                  示例: 1/

                  private JFieldVar generatedField;
                  

                  2/

                  String className = "class name";
                          /* package name */
                          JPackage jp = jCodeModel._package("package name ");
                           /*  class name  */
                          JDefinedClass jclass = jp._class(className);
                          /* add comment */
                          JDocComment jDocComment = jclass.javadoc();
                          jDocComment.add("By AUTOMAT D.I.T tools : " + new Date() +" => " + className);
                          // génération des getter & setter & attribues
                  
                              // create attribue 
                               this.generatedField = jclass.field(JMod.PRIVATE, Integer.class) 
                                       , "attribue name ");
                               // getter
                               JMethod getter = jclass.method(JMod.PUBLIC, Integer.class) 
                                       , "attribue name ");
                               getter.body()._return(this.generatedField);
                               // setter
                               JMethod setter = jclass.method(JMod.PUBLIC, Integer.class) 
                                       ,"attribue name ");
                               // create setter paramétre 
                               JVar setParam = setter.param(getTypeDetailsForCodeModel(Integer.class,"param name");
                               // affectation  ( this.param = setParam ) 
                               setter.body().assign(JExpr._this().ref(this.generatedField), setParam);
                  
                          jCodeModel.build(new File("path c://javaSrc//"));
                  

                  【讨论】:

                    【解决方案15】:

                    这是一个看起来很有趣的 JSON-to-POJO 项目:

                    http://www.jsonschema2pojo.org/

                    【讨论】:

                      猜你喜欢
                      • 2011-04-02
                      • 1970-01-01
                      • 2014-07-03
                      • 1970-01-01
                      • 2012-12-24
                      • 2013-11-06
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-03-15
                      相关资源
                      最近更新 更多