简体   繁体   English

运行时获取Maven神器版本

[英]Get Maven artifact version at runtime

I have noticed that in a Maven artifact's JAR, the project.version attribute is included in two files:我注意到在 Maven 工件的 JAR 中,project.version 属性包含在两个文件中:

META-INF/maven/${groupId}/${artifactId}/pom.properties
META-INF/maven/${groupId}/${artifactId}/pom.xml

Is there a recommended way to read this version at runtime?有没有推荐的方法在运行时阅读这个版本?

You should not need to access Maven-specific files to get the version information of any given library/class.您不需要访问特定于 Maven 的文件来获取任何给定库/类的版本信息。

You can simply use getClass().getPackage().getImplementationVersion() to get the version information that is stored in a .jar-files MANIFEST.MF .您可以简单地使用getClass().getPackage().getImplementationVersion()来获取存储在 .jar 文件MANIFEST.MF的版本信息。 Luckily Maven is smart enough Unfortunately Maven does not write the correct information to the manifest as well by default! 幸运的是 Maven 足够聪明 不幸的是,默认情况下 Maven 也不会将正确的信息写入清单!

Instead one has to modify the <archive> configuration element of the maven-jar-plugin to set addDefaultImplementationEntries and addDefaultSpecificationEntries to true , like this:相反,必须修改maven-jar-plugin<archive>配置元素以将addDefaultImplementationEntriesaddDefaultSpecificationEntries设置为true ,如下所示:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

Ideally this configuration should be put into the company pom or another base-pom.理想情况下,此配置应放入公司pom或其他 base-pom。

Detailed documentation of the <archive> element can be found in the Maven Archive documentation . <archive>元素的详细文档可以在Maven Archive 文档 中找到

To follow up the answer above, for a .war artifact, I found I had to apply the equivalent configuration to maven-war-plugin , rather than maven-jar-plugin :为了跟进上面的答案,对于.war工件,我发现我必须将等效配置应用于maven-war-plugin ,而不是maven-jar-plugin

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

This added the version information to MANIFEST.MF in the project's .jar (included in WEB-INF/lib of the .war )这将版本信息添加到项目.jar中的MANIFEST.MF (包含在.war WEB-INF/lib中)

Here's a method for getting the version from the pom.properties, falling back to getting it from the manifest这是一种从 pom.properties 获取版本的方法,返回到从清单中获取它

public synchronized String getVersion() {
    String version = null;

    // try to load from maven properties first
    try {
        Properties p = new Properties();
        InputStream is = getClass().getResourceAsStream("/META-INF/maven/com.my.group/my-artefact/pom.properties");
        if (is != null) {
            p.load(is);
            version = p.getProperty("version", "");
        }
    } catch (Exception e) {
        // ignore
    }

    // fallback to using Java API
    if (version == null) {
        Package aPackage = getClass().getPackage();
        if (aPackage != null) {
            version = aPackage.getImplementationVersion();
            if (version == null) {
                version = aPackage.getSpecificationVersion();
            }
        }
    }

    if (version == null) {
        // we could not compute the version so use a blank
        version = "";
    }

    return version;
} 

I am using maven-assembly-plugin for my maven packaging.我正在使用maven-assembly-plugin进行 maven 包装。 The usage of Apache Maven Archiver in Joachim Sauer's answer could also work:Joachim Sauer 的回答中使用Apache Maven Archiver也可以:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution .../>
    </executions>
</plugin>

Because archiever is one of maven shared components , it could be used by multiple maven building plugins, which could also have conflict if two or more plugins introduced, including archive configuration inside.因为archiever是maven共享组件之一,它可以被多个maven构建插件使用,如果引入两个或多个插件也会产生冲突,包括里面的archive配置。

I spent some time on the two main approaches here and they didn't work-out for me.我在这两种主要方法上花了一些时间,但它们对我不起作用。 I am using Netbeans for the builds, may be there's more going on there.我正在使用 Netbeans 进行构建,可能还有更多内容。 I had some errors and warnings from Maven 3 with some constructs, but I think those were easy to correct.我有一些来自 Maven 3 的错误和警告以及一些构造,但我认为这些很容易纠正。 No biggie.没什么大不了的。

I did find an answer that looks maintainable and simple to implement in this article on DZone:我确实在 DZone 上的这篇文章中找到了一个看起来可维护且易于实现的答案:

I already have a resources/config sub-folder, and I named my file: app.properties, to better reflect the kind of stuff we may keep there (like a support URL, etc.).我已经有一个 resources/config 子文件夹,我将我的文件命名为:app.properties,以更好地反映我们可能保留在那里的内容(如支持 URL 等)。

The only caveat is that Netbeans gives a warning that the IDE needs filtering off.唯一需要注意的是,Netbeans 会发出 IDE 需要过滤掉的警告。 Not sure where/how.不确定在哪里/如何。 It has no effect at this point.在这一点上它没有任何影响。 Perhaps there's a work around for that if I need to cross that bridge.如果我需要过那座桥,也许可以解决这个问题。 Best of luck.祝你好运。

To get this running in Eclipse, as well as in a Maven build, you should add the addDefaultImplementationEntries and addDefaultSpecificationEntries pom entries as described in other replies, then use the following code:要使其在 Eclipse 以及 Maven 构建中运行,您应该添加addDefaultImplementationEntriesaddDefaultSpecificationEntries pom 条目,如其他回复中所述,然后使用以下代码:

public synchronized static final String getVersion() {
    // Try to get version number from pom.xml (available in Eclipse)
    try {
        String className = getClass().getName();
        String classfileName = "/" + className.replace('.', '/') + ".class";
        URL classfileResource = getClass().getResource(classfileName);
        if (classfileResource != null) {
            Path absolutePackagePath = Paths.get(classfileResource.toURI())
                    .getParent();
            int packagePathSegments = className.length()
                    - className.replace(".", "").length();
            // Remove package segments from path, plus two more levels
            // for "target/classes", which is the standard location for
            // classes in Eclipse.
            Path path = absolutePackagePath;
            for (int i = 0, segmentsToRemove = packagePathSegments + 2;
                    i < segmentsToRemove; i++) {
                path = path.getParent();
            }
            Path pom = path.resolve("pom.xml");
            try (InputStream is = Files.newInputStream(pom)) {
                Document doc = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder().parse(is);
                doc.getDocumentElement().normalize();
                String version = (String) XPathFactory.newInstance()
                        .newXPath().compile("/project/version")
                        .evaluate(doc, XPathConstants.STRING);
                if (version != null) {
                    version = version.trim();
                    if (!version.isEmpty()) {
                        return version;
                    }
                }
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Try to get version number from maven properties in jar's META-INF
    try (InputStream is = getClass()
        .getResourceAsStream("/META-INF/maven/" + MAVEN_PACKAGE + "/"
                + MAVEN_ARTIFACT + "/pom.properties")) {
        if (is != null) {
            Properties p = new Properties();
            p.load(is);
            String version = p.getProperty("version", "").trim();
            if (!version.isEmpty()) {
                return version;
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Fallback to using Java API to get version from MANIFEST.MF
    String version = null;
    Package pkg = getClass().getPackage();
    if (pkg != null) {
        version = pkg.getImplementationVersion();
        if (version == null) {
            version = pkg.getSpecificationVersion();
        }
    }
    version = version == null ? "" : version.trim();
    return version.isEmpty() ? "unknown" : version;
}

If your Java build puts target classes somewhere other than "target/classes", then you may need to adjust the value of segmentsToRemove.如果您的 Java 构建将目标类放在“目标/类”之外的某个地方,那么您可能需要调整 segmentToRemove 的值。

On my spring boot application, the solution from the accepted answer worked until I recently updated my jdk to version 12. Tried all the other answers as well and couldn't get that to work.在我的 spring 启动应用程序中,接受的答案中的解决方案一直有效,直到我最近将 jdk 更新到版本 12。也尝试了所有其他答案,但无法使其正常工作。

At that point, I added the below line to the first class of my spring boot application, just after the annotation @SpringBootApplication那时,我将下面的行添加到我的 Spring Boot 应用程序的第一类中,就在注释@SpringBootApplication

@PropertySources({ 
        @PropertySource("/META-INF/maven/com.my.group/my-artefact/pom.properties")
})

Later I use the below to get the value from the properties file in whichever class I want to use its value and appVersion gets the project version to me:后来我使用下面的从我想使用它的值的任何类中的属性文件中获取值,并且appVersion将项目版本提供给我:

@Value("${version}")
private String appVersion;

Hope that helps someone.希望能帮助某人。

A simple solution which is Maven compatible and works for any (thus also third party) class:一个简单的解决方案,它与 Maven 兼容并适用于任何(因此也是第三方)类:

    private static Optional<String> getVersionFromManifest(Class<?> clazz) {
        try {
            File file = new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
            if (file.isFile()) {
                JarFile jarFile = new JarFile(file);
                Manifest manifest = jarFile.getManifest();
                Attributes attributes = manifest.getMainAttributes();
                final String version = attributes.getValue("Bundle-Version");
                return Optional.of(version);
            }
        } catch (Exception e) {
            // ignore
        }
        return Optional.empty();
    }

If you happen to use Spring Boot you can make use of the BuildProperties class.如果您碰巧使用 Spring Boot,则可以使用BuildProperties类。

Take the following snippet from our OpenAPI configuration class as an example:以我们的 OpenAPI 配置类中的以下片段为例:

@Configuration
@RequiredArgsConstructor // <- lombok
public class OpenApi {

    private final BuildProperties buildProperties; // <- you can also autowire it

    @Bean
    public OpenAPI yourBeautifulAPI() {
        return new OpenAPI().info(new Info()
            .title(buildProperties.getName())
            .description("The description")
            .version(buildProperties.getVersion())
            .license(new License().name("Your company")));
    }
}

I know it's a very late answer but I'd like to share what I did as per this link:我知道这是一个很晚的答案,但我想分享我按照这个链接所做的:

I added the below code to the pom.xml:我将以下代码添加到 pom.xml 中:

        <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>

And this Advice Controller in order to get the version as model attribute:这个 Advice Controller 以获取版本作为模型属性:

import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

@ControllerAdvice
public class CommonControllerAdvice
{
       @Autowired
       BuildProperties buildProperties;
    
       @ModelAttribute("version")
       public String getVersion() throws IOException
       {
          String version = buildProperties.getVersion();
          return version;
       }
    }

The most graceful solutions I've found is that one from J.Chomel : link我发现的最优雅的解决方案是来自J.Chomel 的一个:链接

Doesn't require any hacks with properties.不需要对属性进行任何黑客攻击。 To avoid issues with broken link in a future I'll duplicate it here:为避免将来出现链接断开的问题,我将在此处复制它:

YourClass.class.getPackage().getImplementationVersion();

And (if you don't have Manifest file in your jar/war yet, for me Intellij Idea's Maven already included them) you will require also a small change in pom.xml:并且(如果你的 jar/war 中还没有 Manifest 文件,对我来说,Intellij Idea 的 Maven 已经包含了它们)你还需要对 pom.xml 做一个小改动:

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
     ...
      <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.2</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
                <archive>
                    <manifest>
                        <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    ...

Tried all the answers above but nothing worked for me:尝试了上面的所有答案,但对我没有任何帮助:

  • I did not use Spring我没有使用弹簧
  • Managed to put Version inside of manifest, but someClass.class.getPackage().getImplementationVersion() returned null设法将版本放在清单中,但someClass.class.getPackage().getImplementationVersion()返回 null

However version was appended to the jar file name so I was able to find a jar file using:但是版本被附加到jar文件名中,因此我能够使用以下命令找到 jar 文件:

new File(ClassLoader.getSystemResource("").toURI()).getParentFile();

and then extract it from the file name.然后从文件名中提取它。

It's very easy and no configuration is needed if you use Spring with Maven.如果您将 Spring 与 Maven 一起使用,则非常简单,无需配置。 According to the “Automatic Property Expansion Using Maven” official documentation you can automatically expand properties from the Maven project by using resource filtering.根据“Automatic Property Expansion Using Maven”官方文档,您可以使用资源过滤从 Maven 项目中自动扩展属性。 If you use the spring-boot-starter-parent, you can then refer to your Maven 'project properties' with @..@ placeholders, as shown in the following example:如果您使用 spring-boot-starter-parent,则可以使用 @..@ 占位符引用您的 Maven '项目属性',如下例所示:

project.version=@project.version@
project.artifactId=@project.artifactId@

And you can retrieve it with @Value annotation in any class:您可以在任何 class 中使用 @Value 注释检索它:

@Value("${project.artifactId}@${project.version}")
private String RELEASE;

I hope this helps!我希望这有帮助!

Java 8 variant for EJB in war file with maven project.带有 Maven 项目的 war 文件中 EJB 的 Java 8 变体。 Tested on EAP 7.0.在 EAP 7.0 上测试。

@Log4j // lombok annotation
@Startup
@Singleton
public class ApplicationLogic {

    public static final String DEVELOPMENT_APPLICATION_NAME = "application";

    public static final String DEVELOPMENT_GROUP_NAME = "com.group";

    private static final String POM_PROPERTIES_LOCATION = "/META-INF/maven/" + DEVELOPMENT_GROUP_NAME + "/" + DEVELOPMENT_APPLICATION_NAME + "/pom.properties";

    // In case no pom.properties file was generated or wrong location is configured, no pom.properties loading is done; otherwise VERSION will be assigned later
    public static String VERSION = "No pom.properties file present in folder " + POM_PROPERTIES_LOCATION;

    private static final String VERSION_ERROR = "Version could not be determinated";

    {    
        Optional.ofNullable(getClass().getResourceAsStream(POM_PROPERTIES_LOCATION)).ifPresent(p -> {

            Properties properties = new Properties();

            try {

                properties.load(p);

                VERSION = properties.getProperty("version", VERSION_ERROR);

            } catch (Exception e) {

                VERSION = VERSION_ERROR;

                log.fatal("Unexpected error occured during loading process of pom.properties file in META-INF folder!");
            }
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM