【问题标题】:Retrieve version from maven pom.xml in code从代码中的 maven pom.xml 检索版本
【发布时间】:2011-04-11 11:29:58
【问题描述】:

在代码中(即以编程方式)从 maven 的 pom.xml 中检索版本号的最简单方法是什么?

【问题讨论】:

    标签: java maven version


    【解决方案1】:

    假设您使用的是 Java,您可以:

    1. 在(最常见的)src/main/resources 目录中创建一个.properties 文件(但在第 4 步中,您可以告诉它到别处查找)。

    2. 使用项目版本的标准 Maven 属性在 .properties 文件中设置某些属性的值:

      foo.bar=${project.version}
      
    3. 在您的 Java 代码中,将属性文件中的值作为类路径中的资源加载(谷歌有很多关于如何执行此操作的示例,但 here's an example for starters)。

    4. 在 Maven 中,启用资源过滤。这将导致 Maven 将该文件复制到您的输出类中,并在该复制期间翻译资源,解释属性。你可以找到一些信息 here 但你大多只是在你的 pom 中这样做:

      <build>
        <resources>
          <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
          </resource>
        </resources>   
      </build>
      

    您还可以访问其他标准属性,例如 project.nameproject.description,甚至是您放入 pom &lt;properties&gt; 等的任意属性。资源过滤与 Maven 配置文件相结合,可以在以下位置为您提供可变的构建行为建造时间。当您在运行时使用 -PmyProfile 指定配置文件时,可以启用随后可以显示在您的构建中的属性。

    【讨论】:

    • 我找到了一个代码this 没有改变 Maven 配置。
    • 谨防直接在src/main/resources 上使用过滤,因为这可能会处理此目录中的所有文件,包括二进制文件。为了避免不可预知的行为,最好在src/main/resources-filtered 目录as suggested here 上进行过滤。无论如何,谢谢你的好把戏!
    • 下面使用 MavenXppReader 获取实际模型的答案非常有用,因为它不需要运行任何东西来查找值。如果您需要在运行任何内容之前知道版本,请查看以下答案;让 gradle 知道签出的 maven 项目的版本对我很有帮助,这样我就可以事先知道输出 jar 的位置。
    • 请注意,如果在单元测试期间使用它,这将不起作用。如果您在单元测试期间需要项目信息,请参阅@kriegaex 的回答
    • 来自here,如果你使用spring boot,需要使用@project.version@而不是${project.version}
    【解决方案2】:

    接受的答案可能是静态将版本号放入应用程序的最佳和最稳定的方法,但实际上并没有回答原始问题:如何检索pom.xml 中工件的版本号?因此,我想提供一种替代方法,展示如何在运行时动态

    您可以使用 Maven 本身。更准确地说,您可以使用 Maven 库。

    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-model</artifactId>
      <version>3.3.9</version>
    </dependency>
    

    然后在 Java 中做这样的事情:

    package de.scrum_master.app;
    
    import org.apache.maven.model.Model;
    import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
    import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    public class Application {
        public static void main(String[] args) throws IOException, XmlPullParserException {
            MavenXpp3Reader reader = new MavenXpp3Reader();
            Model model = reader.read(new FileReader("pom.xml"));
            System.out.println(model.getId());
            System.out.println(model.getGroupId());
            System.out.println(model.getArtifactId());
            System.out.println(model.getVersion());
        }
    }
    

    控制台日志如下:

    de.scrum-master.stackoverflow:my-artifact:jar:1.0-SNAPSHOT
    de.scrum-master.stackoverflow
    my-artifact
    1.0-SNAPSHOT
    

    2017-10-31 更新:为了回答 Simon Sobisch 的后续问题,我将示例修改如下:

    package de.scrum_master.app;
    
    import org.apache.maven.model.Model;
    import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
    import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class Application {
      public static void main(String[] args) throws IOException, XmlPullParserException {
        MavenXpp3Reader reader = new MavenXpp3Reader();
        Model model;
        if ((new File("pom.xml")).exists())
          model = reader.read(new FileReader("pom.xml"));
        else
          model = reader.read(
            new InputStreamReader(
              Application.class.getResourceAsStream(
                "/META-INF/maven/de.scrum-master.stackoverflow/aspectj-introduce-method/pom.xml"
              )
            )
          );
        System.out.println(model.getId());
        System.out.println(model.getGroupId());
        System.out.println(model.getArtifactId());
        System.out.println(model.getVersion());
      }
    }
    

    【讨论】:

    • 这与我使用的几乎相同,并且在从 Eclipse 启动时工作正常,但从正常的 packaged jar 启动时却没有(依赖类未集成)并且没有使用 maven-assembly-plugin jar-with-dependencies 打包时工作我得到一个 java.io.FileNotFoundException: pom.xml(它在最终的 jar 中为 META-INF/maven/my.package/myapp/pom.xml) - 任何提示如何解决这个问题?
    • 我的解决方案旨在在开发环境中动态工作,例如在从 IDE 或控制台启动的测试或工具中使用时。该问题的公认答案显示了将版本号静态打包到工件中的几种方法。我根本没有假设 pom.xml 在 JAR 中可用。不过,很高兴你有它。也许您可以在打开文件阅读器时调整路径,并使其取决于类加载器的情况。我将不得不为自己尝试。如果这没有帮助,请随时提出后续问题。
    • 嗨@SimonSobisch,我刚刚更新了我的答案,以便向您展示如何做您想做的事。但请注意,我只是做的又快又脏,我不是特别喜欢带有嵌套构造函数的代码。
    【解决方案3】:

    打包的工件包含一个META-INF/maven/${groupId}/${artifactId}/pom.properties 文件,其内容如下:

    #Generated by Maven
    #Sun Feb 21 23:38:24 GMT 2010
    version=2.5
    groupId=commons-lang
    artifactId=commons-lang
    

    许多应用程序使用此文件在运行时读取应用程序/jar 版本,需要零设置。

    上述方法的唯一问题是该文件(当前)是在package 阶段生成的,因此在测试等期间不会出现(有一个 Jira 问题可以更改此设置,请参阅MJAR-76) .如果这对您来说是个问题,那么 Alex 描述的方法就是要走的路。

    【讨论】:

    • 对于寻找阅读属性示例的人,这篇文章介绍了几种方法 - javablog.fr/…
    【解决方案4】:

    还有Easy way to display your apps version number using Maven中描述的方法:

    将此添加到 pom.xml

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <configuration>
            <archive>
              <manifest>
                <mainClass>test.App</mainClass>
                <addDefaultImplementationEntries>
                  true
                </addDefaultImplementationEntries>
              </manifest>
            </archive>
          </configuration>
        </plugin>
      </plugins>
    </build>
    

    然后使用这个:

    App.class.getPackage().getImplementationVersion()
    

    我发现这种方法更简单。

    【讨论】:

    • -1 - 这个解决方案对我不起作用; getImplementationVersion() 的值为 null。 (maven 3.0.4 版)
    • 取决于阶段...仅在工件被打包时有效,因此不适用于单元测试:-/
    • 对于.war 工件,记得使用maven-war-plugin 而不是maven-jar-plugin
    • 对我来说,这在 Tomcat 8 中有效,但在 Tomcat 7 中无效(getImplementationVersion() 返回 null)。跨度>
    【解决方案5】:

    如果使用 jar 或 war 等 mvn 封装,请使用:

    getClass().getPackage().getImplementationVersion()
    

    它读取存档中生成的 META-INF/MANIFEST.MF(设置为 pom.xml 的版本)的属性“Implementation-Version”。

    【讨论】:

    • 如果想在 MANIFEST.MF 中包含“实施版本”:stackoverflow.com/questions/921667/…
    • 这似乎是一个不完整的答案,仅适用于某些情况。不适用于我的 TestNG 项目,可能是由于缺少清单包装。
    【解决方案6】:

    为了补充 @kieste 发布的内容,如果您使用 Spring-boot,我认为这是在代码中提供 Maven 构建信息的最佳方式:http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-application-info 的文档非常有用。

    您只需要激活执行器,并在application.propertiesapplication.yml 中添加您需要的属性

    Automatic property expansion using Maven
    
    You can automatically expand info properties from the Maven project using resource filtering. If you use the spring-boot-starter-parent you can then refer to your Maven ‘project properties’ via @..@ placeholders, e.g.
    
    project.artifactId=myproject
    project.name=Demo
    project.version=X.X.X.X
    project.description=Demo project for info endpoint
    info.build.artifact=@project.artifactId@
    info.build.name=@project.name@
    info.build.description=@project.description@
    info.build.version=@project.version@
    

    【讨论】:

    • 这个答案有助于我需要使用 @..@ 表示法从 maven 读取属性。其他东西正在使用 ${..} 表示法,它是冲突的。
    【解决方案7】:

    有时,在编写与项目版本相关的脚本时,Maven 命令行就足够了,例如通过存储库中的 URL 检索工件:

    mvn help:evaluate -Dexpression=project.version -q -DforceStdout
    

    使用示例:

    VERSION=$( mvn help:evaluate -Dexpression=project.version -q -DforceStdout )
    ARTIFACT_ID=$( mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout )
    GROUP_ID_URL=$( mvn help:evaluate -Dexpression=project.groupId -q -DforceStdout | sed -e 's#\.#/#g' )
    curl -f -S -O http://REPO-URL/mvn-repos/${GROUP_ID_URL}/${ARTIFACT_ID}/${VERSION}/${ARTIFACT_ID}-${VERSION}.jar
    

    【讨论】:

    • 这很好,只是速度慢得令人难以置信,尤其是在尚未下载工件的情况下。
    • 我完全同意 :-( 唯一的优点是它适用于各种 Maven 项目/模块,即使是那些从某些父 pom.xml 继承其版本的项目/模块
    【解决方案8】:

    使用 spring boot 时,此链接可能有用:https://docs.spring.io/spring-boot/docs/2.3.x/reference/html/howto.html#howto-properties-and-configuration

    使用 spring-boot-starter-parent 您只需将以下内容添加到您的应用程序配置文件中:

    # get values from pom.xml
    pom.version=@project.version@
    

    之后该值是这样可用的:

    @Value("${pom.version}")
    private String pomVersion;
    

    【讨论】:

    • 使用 gradle 可以吗?
    【解决方案9】:

    使用这个库来简化一个简单的解决方案。将您需要的任何内容添加到清单中,然后按字符串查询。

     System.out.println("JAR was created by " + Manifests.read("Created-By"));
    

    http://manifests.jcabi.com/index.html

    【讨论】:

      【解决方案10】:
          <build>
                  <finalName>${project.artifactId}-${project.version}</finalName>
                  <pluginManagement>
                      <plugins>
                          <plugin>
                              <groupId>org.apache.maven.plugins</groupId>
                              <artifactId>maven-war-plugin</artifactId>
                              <version>3.2.2</version>
                              <configuration>
                                  <failOnMissingWebXml>false</failOnMissingWebXml>
                                  <archive>
                                      <manifest>
                                          <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                                          <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                                      </manifest>
                                  </archive>
                              </configuration>
                          </plugin>
                       </plugins>
                  </pluginManagement>
      </build>
      

      使用this.getClass().getPackage().getImplementationVersion()获取版本

      PS 别忘了补充:

      <manifest>
          <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
          <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
      </manifest>
      

      【讨论】:

      • 谢谢。这适用于我在 RapidClipse / Vaadin + Maven 3.6.3 中的项目
      【解决方案11】:

      我在白天的工作中遇到了同样的问题。尽管许多答案将有助于找到特定工件的版本,但我们需要获取不直接依赖于应用程序的模块/jar 的版本。应用启动时classpath由多个模块组装而成,主应用模块不知道以后添加了多少jar。

      这就是为什么我想出了一个不同的解决方案,它可能比必须从 jar 文件中读取 XML 或属性更优雅一些。

      想法

      1. 使用 Java 服务加载器方法可以在以后添加尽可能多的组件/工件,它们可以在运行时贡献自己的版本。创建一个非常轻量级的库,只需几行代码即可读取、查找、过滤和排序类路径上的所有工件版本。
      2. 创建一个 maven 源代码生成器插件,在编译时为每个模块生成服务实现,在每个 jar 中打包一个非常简单的服务。

      解决办法

      解决方案的第一部分是artifact-version-service 库,现在可以在githubMavenCentral 上找到它。它涵盖了服务定义和一些在运行时获取工件版本的方法。

      第二部分是artifact-version-maven-plugin,也可以在githubMavenCentral 上找到。它用于让生成器轻松实现每个工件的服务定义。

      示例

      获取所有带有坐标的模块

      不再需要读取jar manifest,只需一个简单的方法调用:

      // iterate list of artifact dependencies
      for (Artifact artifact : ArtifactVersionCollector.collectArtifacts()) {
          // print simple artifact string example
          System.out.println("artifact = " + artifact);
      }
      

      返回一组排序的工件。要修改排序顺序,请提供自定义比较器:

      new ArtifactVersionCollector(Comparator.comparing(Artifact::getVersion)).collect();
      

      这样返回的工件列表按版本号排序。

      查找特定的工件

      ArtifactVersionCollector.findArtifact("de.westemeyer", "artifact-version-service");
      

      获取特定工件的版本详细信息。

      查找具有匹配 groupId(s) 的工件

      查找所有 groupId de.westemeyer 的工件(完全匹配):

      ArtifactVersionCollector.findArtifactsByGroupId("de.westemeyer", true);
      

      查找 groupId de.westemeyer 开头的所有工件:

      ArtifactVersionCollector.findArtifactsByGroupId("de.westemeyer", false);
      

      按版本号排序结果:

      new ArtifactVersionCollector(Comparator.comparing(Artifact::getVersion)).artifactsByGroupId("de.", false);
      

      对工件列表实施自定义操作

      通过提供 lambda,第一个示例可以这样实现:

      ArtifactVersionCollector.iterateArtifacts(a -> {
          System.out.println(a);
          return false;
      });
      

      安装

      将这两个标签添加到所有 pom.xml 文件中,或者添加到某处的公司主 pom 中:

      <build>
        <plugins>
          <plugin>
            <groupId>de.westemeyer</groupId>
            <artifactId>artifact-version-maven-plugin</artifactId>
            <version>1.1.0</version>
            <executions>
              <execution>
                <goals>
                  <goal>generate-service</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      
      <dependencies>
        <dependency>
          <groupId>de.westemeyer</groupId>
          <artifactId>artifact-version-service</artifactId>
          <version>1.1.0</version>
        </dependency>
      </dependencies>
      

      反馈

      如果有人可以尝试一下解决方案,那就太好了。获得有关您认为该解决方案是否符合您的需求的反馈会更好。因此,如果您有任何建议、功能请求、问题等,请不要犹豫,在任何 github 项目上添加新问题。

      许可证

      所有源代码都是开源的,即使是商业产品也可以免费使用(MIT 许可证)。

      【讨论】:

      • 这个不错,去试试
      • 太好了,让我知道它是否适合你!
      • 嘿@jj33,我创建了一个新版本,以包含您在 github 上建议的其他字段。希望对你有用!
      【解决方案12】:

      第 1 步:如果您使用的是 Spring Boot,您的 pom.xml 应该已经包含 spring-boot-maven-plugin。您只需要添加以下配置即可。

      <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <executions>
              <execution>
                  <id>build-info</id>
                  <goals>
                      <goal>build-info</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
      

      它指示插件也执行 build-info 目标,默认情况下不运行。这会生成有关您的应用程序的构建元数据,其中包括工件版本、构建时间等。

      第 2 步: 使用 buildProperties bean 访问构建属性。在我们的例子中,我们创建了一个 restResource 来访问我们的 webapp 中的构建信息

      @RestController
      @RequestMapping("/api")
      public class BuildInfoResource {
          @Autowired
          private BuildProperties buildProperties;
      
          
          @GetMapping("/build-info")
          public ResponseEntity<Map<String, Object>> getBuildInfo() {
              Map<String, String> buildInfo = new HashMap();
              buildInfo.put("appName", buildProperties.getName());
              buildInfo.put("appArtifactId", buildProperties.getArtifact());
              buildInfo.put("appVersion", buildProperties.getVersion());
              buildInfo.put("appBuildDateTime", buildProperties.getTime());
              return ResponseEntity.ok().body(buldInfo);
          }
      }
      

      我希望这会有所帮助

      【讨论】:

        【解决方案13】:

        参考ketankk's answer

        不幸的是,添加此内容会影响我的应用程序处理资源的方式:

        <build>
          <resources>
            <resource>
              <directory>src/main/resources</directory>
              <filtering>true</filtering>
            </resource>
          </resources>   
        </build>
        

        但是在 maven-assemble-plugin 的 标签中使用这个就可以了:

        <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
        <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
        

        所以我可以使用

        String version = getClass().getPackage().getImplementationVersion();
        

        【讨论】:

          【解决方案14】:

          前言: 因为我记得几年前answered 之后经常提到的这个问题,显示了一个动态版本实际上动态访问 Maven POM 信息(例如在测试期间),今天我发现了一个similar question,它涉及从另一个模块 B 访问模块 A 的 Maven 信息。

          想了想,不由自主地产生了使用特殊注解的想法,将其应用到package-info.java中的包声明中。我还在 GitHub 上创建了一个多模块示例项目。我不想重复整个答案,所以请参阅this answer 中的解决方案 B。 Maven 设置涉及模板化 Maven 插件,但也可以使用资源过滤和通过 Build Helper Maven 将生成的源目录添加到构建的组合以更详细的方式解决。我想避免这种情况,所以我只是使用了 Maven 模板。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-06
            • 1970-01-01
            • 2014-10-28
            • 1970-01-01
            • 2014-03-11
            • 2018-04-28
            相关资源
            最近更新 更多