【问题标题】:Generate a Version.java file in Maven在 Maven 中生成一个 Version.java 文件
【发布时间】:2011-01-29 00:30:10
【问题描述】:

我有一个使用 Ant 脚本构建的 Java 项目。我正在尝试将项目转换为 Maven。

其中一项任务生成一个名为 Version.java 的 Java 源文件,其中包含编译时间戳的静态字符串表示,如下所示:

package com.foo.bar;
public final class Version {
 public static String VERSION="100301.1046";
}

Ant 任务很简单:

<target name="version" depends="init" description="Create Version.java">
    <echo file="src/${package.dir}/Version.java" message="package ${package.name};${line.separator}" />
    <echo file="src/${package.dir}/Version.java" append="true" message="public final class Version {${line.separator}" />
    <echo file="src/${package.dir}/Version.java"
          append="true"
          message=" public static String VERSION=&quot;${buildtime}&quot;;${line.separator}" />
    <echo file="src/${package.dir}/Version.java" append="true" message="}${line.separator}" />
    <echo message="BUILD ${buildtime}" />
</target>

是否可以在 Maven 中使用 generate-sources 或其他一些简单的方法做类似的事情?

【问题讨论】:

    标签: java maven-2 code-generation


    【解决方案1】:

    我认为这不是解决此类问题的好方法。

    更好的方法是将版本信息放在一个properties 文件中,您的Java 程序将读取该文件:

    您的属性文件将包含以下行:

    myapp.version=${project.version}
    

    然后,在您的pom.xml 中,指明该文件将是 Maven 的filtered

    <resources>
        <resource>
            <directory>the/directory/that/contains/your/properties/file</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    

    当 Maven 构建您的应用程序时,它会将所有 ${...} 替换为它们的值。默认情况下,${project.version} 定义了pom.xml 的版本(即&lt;version&gt; 标签的值)。

    然后,在您的 Java 代码中,您只需加载 properties 文件并检索 myApp.version 属性值。

    请注意,您可以使用 Build Number plugin 设置比当前版本更“复杂”的内容(例如,如果您想将构建时间放在您的属性中)。

    【讨论】:

    • 我认为这种方法很好,但当您有 @PersistentUnit(value="myPU") 之类的注释时不适用。你怎么看这个案子?
    • 如果使用 maven 3.x,请将 ${pom.version}(现已弃用)替换为 ${project.version}docs.codehaus.org/display/MAVENUSER/MavenPropertiesGuide
    • 为什么不是解决的好办法?我有多个带有不相交的属性文件集的模块。我认为存储到多个属性文件是错误的方式。
    • @mirelon 出于多种原因,可维护性是其中之一。从 Ant 任务中创建包含 String 连接的 Java 文件有点奇怪,而且您不能确定不会编写无效代码。 Maven 过滤器正是这个目的,为什么不使用它呢?
    • 属性文件可以被修改或丢失。最好将它嵌入到二进制文件中。
    【解决方案2】:

    如果你觉得蚂蚁有点丑,也可以使用maven-replacer-plugin: pom 条目可能是:

    <project>
      ...
      <properties>
        <version.template.file>src/main/java/com/stackoverflowVersion.java.template</version.template.file>
    <version.file>src/main/java/com/stackoverflow/Version.java</version.file>
      </properties>
      ...
      <build>
        <plugins>
          <plugin>
            <groupId>com.google.code.maven-replacer-plugin</groupId>
                <artifactId>maven-replacer-plugin</artifactId>
                <version>1.4.0</version>
                <executions>                
                    <execution>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>replace</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <file>${version.template.file}</file>
                    <outputFile>${version.file}</outputFile>
                    <replacements>
                        <replacement>
                            <token>@buildnumber@</token>
                            <value>${svn.revision}</value>
                        </replacement>
                        <replacement>
                            <token>@buildtime@</token>
                            <value>${maven.build.timestamp}</value>
                        </replacement>
                        <replacement>
                            <token>@pomversion@</token>
                            <value>${project.version}</value>
                        </replacement>
                    </replacements>                        
                </configuration>
          </plugin>
        </plugins>
      </build>
      ...
    </project>
    

    Version.java.template 可能是:

    package com.stackoverflow;
    
    public final class Version {
    
        public static final String build_number="@buildnumber@";
    
        public static final String build_time="@buildtime@";
    
        public static final String pomversion="@pomversion@";
    
    }
    

    【讨论】:

    • 最好使用“生成源”阶段
    【解决方案3】:

    这是一个老问题,但还有另一个解决方案可以完美地做得很好(在 Maven 意义上):Templating Maven Plugin

    如您所料,使用此插件会将处理后的 Java 文件放入 target/generated-sources 文件夹中。并将generated-sources 下的文件夹添加到构建路径中。 您不会再误签入已处理的文件。

    如何使用

    首先将以下内容放在src/main/java-templates/com/foo/bar/Version.java下:

    package com.foo.bar;
    public final class Version {
        public static final String VERSION = "${project.version}";
    }
    

    然后将以下内容添加到您的 POM:

    <build>
        <plugins>
        ...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>templating-maven-plugin</artifactId>
                <version>1.0.0</version>
                <executions>
                    <execution>
                        <id>filtering-java-templates</id>
                        <goals>
                            <goal>filter-sources</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        ...
        </plugins>
    </build>
    

    文件夹target/generated-sources/java-templates被Maven添加到构建路径中。

    【讨论】:

    • 这适用于我,但我仍然必须手动启动模板:filter-sources。我是Maven的新手。如何在更改配置文件或开始每个构建之前执行此插件?
    • 是的,如果您更改模板,您确实需要调用 Maven 来生成源代码。但是您可以直接拨打mvn install,而不必费心记住您使用的具体目标。
    • @Roel 这个插件将bind itself automatically 进入 Maven 生命周期的第一阶段之一:generate-sources。所以,你实际上不必像 vegemite4me 所说的那样手动调用它
    【解决方案4】:

    这是另一个与拉尔夫自己的答案相同的解决方案, 使用 pom 属性过滤和模板文件:

    模板文件(VersionJava.template放在src/main/resources/version):

    package ${ver.package.name};
    public final class ${ver.class.name} {
        public static String VERSION="${ver.buildtime}";
    }
    

    pom:

    <properties>
        ...
        <ver.package.dir>com/foo/bar${project.artifactId}</ver.package.dir>
        <ver.package.name>com.foo.bar${project.artifactId}</ver.package.name>
        <ver.class.name>Version</ver.class.name>
        <ver.buildtime>${maven.build.timestamp}</ver.buildtime>
        <ver.template.dir>src/main/resources/version</ver.template.dir>
        <ver.template.file>VersionJava.template</ver.template.file>
    </properties>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>version/*</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>${ver.template.dir}</directory>
                <includes>
                    <include>*.java</include>
                </includes>
                <filtering>true</filtering>
                <targetPath>${basedir}/src/main/java/${ver.package.dir}</targetPath>
            </resource>
        </resources>        
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <configuration>
                            <tasks>
                                <copy file="${ver.template.dir}/${ver.template.file}" tofile="${ver.template.dir}/${ver.class.name}.java" />
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                    <execution>
                        <phase>compile</phase>
                        <configuration>
                            <tasks>
                                <delete file="${ver.template.dir}/${ver.class.name}.java" />
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    

    现在这似乎有点过分了,但它用途广泛,而且我最喜欢它 是我有一个可读格式的模板文件(而不是 pom 中的 echo 语句)。 这也允许我修改版本类而无需更改 pom

    【讨论】:

    • 这太棒了!我会将目标文件放在 /target/source/${ver.package.dir} 中,然后(假设 Eclipse)添加 /target/source/${ver.package.dir} 作为源文件夹。这样您就可以将其从源代码管理中排除
    • @john-oxley 我同意。生成的文件应该在目标文件夹而不是 src 中。此外,我认为如果将其放入“target/generated-sources/pom/${ver.package.dir}”中(假设为 NetBeans),它将自动作为源包含在内。我还没有尝试过,但我会的,如果它按预期工作,我应该编辑我的答案:)
    • 此解决方案在 Eclipse 4.2 中使用 m2e 插件导致无限循环。但是,我使用了没有 ant 任务的解决方案。我目前看不到后者的原因:过滤解决方案只是生成具有正确字段的 Version.java。
    【解决方案5】:

    经过更多谷歌搜索,我想出了这个(在 pom.xml 中):

    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.3</version>
        <executions>
          <execution>
            <goals>
              <goal>run</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <tasks>
                <property name="src.dir" value="${project.build.sourceDirectory}" />
                <property name="package.dir" value="com/foo/bar" />
                <property name="package.name" value="com.foo.bar" />
                <property name="buildtime" value="${maven.build.timestamp}" />
    
                <echo file="${src.dir}/${package.dir}/Version.java" message="package ${package.name};${line.separator}" />
                <echo file="${src.dir}/${package.dir}/Version.java" append="true" message="public final class Version {${line.separator}" />
                <echo file="${src.dir}/${package.dir}/Version.java" append="true"
                  message=" public static String VERSION=&quot;${buildtime}&quot;;${line.separator}" />
                <echo file="${src.dir}/${package.dir}/Version.java" append="true" message="}${line.separator}" />
                <echo message="BUILD ${buildtime}" />
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
    

    它似乎运行良好并生成了这个 Java 文件:

    package com.foo.bar;
    public final class Version {
     public static String VERSION="100318.1211";
    }
    

    【讨论】:

    • 这是一个非常糟糕的解决方案。更好地使用 romaintaz 提供的解决方案。为这类东西生成源代码会导致不必要的编译,因为每次都会重新创建 java 文件,这会触发级联编译。请帮您自己和您的同事一个忙,不要这样做!
    • 我正在寻找在webapp root 内创建文本文件 version.txt 的解决方案,我想这是最好的。
    【解决方案6】:

    基于the answer by @superole。这是一个简化版本,无需设置额外的属性。只是将项目的版本复制到 Version.java 中。

    Version.java 放入src/main/templates

    package thepackage;
    
    public final class Version {
    
     public static String VERSION="${project.version}";
    
    }
    

    指示 maven 替换 Version.java 中的标记

    <resources>
        <resource>
            <directory>src/main/templates</directory>
            <includes>
                <include>*.java</include>
            </includes>
            <filtering>true</filtering>
            <targetPath>${project.build.directory}/generated-sources/java/thepackage</targetPath>
        </resource>
    </resources>
    

    指示 maven 知道 generated-sources/java 作为构建路径:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <version>1.8</version>
        <executions>
            <execution>
                 <id>add-source</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>add-source</goal>
                </goals>
                <configuration>
                    <sources>
                        <source>${project.build.directory}/generated-sources/java/</source>
                    </sources>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    最后,让 Eclipse m2e

    • 注意新的构建路径
    • 不要陷入无限循环构建。

    第二点是通过在eclipse的增量构建期间禁用maven-resources-plugin来实现的。

    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.eclipse.m2e</groupId>
                <artifactId>lifecycle-mapping</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <lifecycleMappingMetadata>
                        <pluginExecutions>
                            <pluginExecution>
                              <pluginExecutionFilter>
                                <groupId>org.codehaus.mojo</groupId>
                                <artifactId>build-helper-maven-plugin</artifactId>
                                <versionRange>[1.0,)</versionRange>
                                <goals>
                                  <goal>parse-version</goal>
                                  <goal>add-source</goal>
                                  <goal>maven-version</goal>
                                  <goal>add-resource</goal>
                                  <goal>add-test-resource</goal>
                                  <goal>add-test-source</goal>
                                </goals>
                              </pluginExecutionFilter>
                              <action>
                                <execute>
                                  <runOnConfiguration>true</runOnConfiguration>
                                  <runOnIncremental>true</runOnIncremental>
                                </execute>
                              </action>
                            </pluginExecution>
                            <pluginExecution>
                                <pluginExecutionFilter>
                                    <groupId>org.apache.maven.plugins</groupId>
                                    <artifactId>maven-resources-plugin</artifactId>
                                    <versionRange>[1.0.0,)</versionRange>
                                    <goals>
                                        <goal>resources</goal>
                                    </goals>
                                </pluginExecutionFilter>
                                <action>
                                    <execute>
                                        <runOnConfiguration>true</runOnConfiguration>
                                        <runOnIncremental>false</runOnIncremental>
                                    </execute>
                                </action>
                            </pluginExecution>
                        </pluginExecutions>
                    </lifecycleMappingMetadata>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
    

    thepackage 需要替换为您的包:同时相应地调整targetPath。我发现在targetpath 中设置路径比在src/main/templates 中设置许多子文件夹更容易。

    【讨论】:

      【解决方案7】:

      我正在使用Maven WAR Plugin 将信息添加到MANIFEST.MF 文件,然后在Java 中读取此MANIFEST.MF 文件:

           <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-war-plugin</artifactId>
              <version>2.6</version>
              <configuration>
                 <archive>
                    <manifest>
                       <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                       <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                    </manifest>
                    <manifestEntries>
                       <Build-Time>${maven.build.timestamp}</Build-Time>
                    </manifestEntries>
                 </archive>
              </configuration>
           </plugin>
      

      此配置生成以下 MANIFEST.MF 文件:

      Manifest-Version: 1.0
      Implementation-Title: MyApp
      Implementation-Version: 2.11.0-SNAPSHOT
      Built-By: niestroj
      Specification-Title: MyApp
      Implementation-Vendor-Id: com.mycompany
      Build-Time: 2017-01-09 15:30
      Created-By: Apache Maven 3.0.5
      Build-Jdk: 1.8.0_40
      Specification-Version: 2.11
      

      后来我在 Java 中这样读:

        try {
           Manifest manifest = new Manifest(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"));
           Attributes attributes = manifest.getMainAttributes();
           attributes.getValue("Implementation-Version");
           attributes.getValue("Build-Time");
        } catch (IOException ex) {
           LOGGER.debug("Error reading manifest file information", ex);
        }
      

      【讨论】:

        【解决方案8】:

        正如@Romain 所建议的那样,您可以从属性文件中读取版本(/META-INF/maven/groupId/artifactId/pom.properties,如果您可以等到打包,或者滚动您自己的过滤文件,如果您不能或如果它没有提供您的所有内容需要)。

        您是否想坚持使用实际的 Version 类,然后查看 maven 用户列表中的 this thread,该列表正是为此提出了解决方案(基于您将绑定的 antrun 插件) generated-sources 阶段)。

        【讨论】:

          【解决方案9】:

          现在,只需几行 XML 代码就可以做到这一点的标准方法是使用 templating-maven-plugin。

          Filtering source code in Maven查看我的回答

          一般来说,Maven 的方式是描述你想要做什么然后弄清楚如何。当需要数十或数百行 XML 时,要么找到合适的插件,要么编写它。这就是创建模板 maven-plugin 的基本原理 :-)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-24
            • 1970-01-01
            • 1970-01-01
            • 2013-10-19
            相关资源
            最近更新 更多