在运行时获取Maven工件版本


Answers:


264

您无需访问特定于Maven的文件即可获取任何给定库/类的版本信息。

您可以简单地使用getClass().getPackage().getImplementationVersion()来获取存储在.jar文件中的版本信息MANIFEST.MF幸运的是,Maven足够聪明,不幸的是,默认情况下,Maven也不将正确的信息写入清单!

相反,必须修改的设置<archive>元素maven-jar-pluginto set addDefaultImplementationEntriesaddDefaultSpecificationEntriesto 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>

理想情况下,应将此配置放入公司pom或其他基本pom中。

<archive>元素的详细文档可以在Maven存档文档中找到


6
遗憾的是,并不是每个类加载器似乎都从清单文件中加载这些属性(我记得在这种情况下Tomcat存在问题)。
dwegener 2012年

@avithan:真的吗?我从来没有对Tomcat使用这种方法有任何疑问。另外,我认为忽略清单的类加载器可能不符合要求。
约阿希姆·绍尔

@JoachimSauer好的,我错了。目前看来,它在HotSpot上运行良好,但在OpenJDK上却无法可靠运行。当我获得详细信息时,我将向您报告
dwegener

@avithan这与我有关(我还没有看到您的举报)-您是否已获得详细信息?
托尔比约恩Ravn的安徒生

4
不幸的是,如果该项目是从Eclipse或使用“ mvn exec:java”运行的,则无法使用。
2014年

77

要遵循上述答案,对于.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>

这会将版本信息添加到MANIFEST.MF项目的中.jar(包含在WEB-INF/lib.war


3
在我的情况下,<archiveClasses> true </ archiveClasses>导致了错误。但是问题解决了stackoverflow.com/questions/14934299/…–
保罗·韦雷斯特

10
当我尝试此操作时,null尽管war文件中的MANIFEST.MF包含正确的信息,但我的结果始终是。
thomas.mc.work 2014年

我还需要将其添加到maven-assembly-plugin
acheron55

2
<archiveClasses> true </ archiveClasses>似乎无关
KarlKildén2015年

1
@RafaelSimonelli我已经删除了<archiveClasses>true</archiveClasses>-从那时起它一直可靠地工作。
thomas.mc.work,

27

这是一种从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;
} 

2
将其放在静态初始化程序块中。
opyate

1
好建议。虽然,如果您在servlet(或.jsp)中使用它,请确保使用getServletContext()。getResourceAsStream而不是getClass()。getResourceAsStream
Sandman

3
这仅在从jar运行应用程序时有效。如果从exec-maven-plugin(例如Netbeans)运行,则资源为null。
Leif Gruenwoldt 2014年

该代码将成为我的主类默认设置的一部分!谢谢!!
温德尔

我将此与Will's答案结合使用,以获得一种直接且易于维护的选项。
javydreamercsw

3

我花了一些时间在这里的两种主要方法上,但它们对我来说并没有解决。我正在使用Netbeans进行构建,可能还有更多正在进行中。我从Maven 3中获得了一些带有某些构造的错误和警告,但是我认为这些错误和警告很容易纠正。没关系

在DZone上的这篇文章中,我确实找到了一个看起来可维护且易于实现的答案:

我已经有一个resources / config子文件夹,并且将我的文件命名为app.properties,以更好地反映我们可能会保留在那里的东西(例如支持URL等)。

唯一需要注意的是,Netbeans发出警告,表明IDE需要过滤掉。不知道在哪里/如何。在这一点上它没有作用。如果我需要跨越那座桥梁,也许可以解决这个问题。祝你好运。


3

我正在使用maven-assembly-plugin我的Maven包装。在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>

由于归档器是maven共享组件之一,因此它可以被多个maven构建插件使用,如果引入了两个或多个插件(包括archive内部配置),它们也可能会发生冲突。


2

要使它在Eclipse和Maven构建中运行,您应该按照其他答复中的描述添加addDefaultImplementationEntriesaddDefaultSpecificationEntriespom条目,然后使用以下代码:

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

如果您的Java构建将目标类放置在“目标/类”之外的其他位置,那么您可能需要调整segmentsToRemove的值。


您知道这是否用于单元测试吗System.getProperty("user.dir")/pom.xml?我很确定,除了WTP以外,它也将用于其他方面。
亚当·根特

仅当您的项目位于目录中时,此方法才有效-如果您正在运行基于jarfiles的项目,则您的解决方案将无法使用。您需要使用.getResource().getResourceAsStream()
卢克·哈奇森

是的,我假设您已经检查过jar(ala getResource)。首先,您要用getResource检查是否失败,然后该项目尚未构建到jar中,这意味着您是从Eclipse或Maven运行它,这意味着`System.getProperty(“ user.dir”)/ pom.xml 。唯一的问题是,该pom文件不是真正的有效pom(即某些属性将不会扩展),但是您通过Eclipse方式获得的文件也不是。
亚当·根特

1

在我的Spring Boot应用程序上,从接受的答案开始一直有效的解决方案一直有效,直到我最近将jdk更新到版本12。也尝试了所有其他答案,并且无法解决该问题。

那时,我将以下行添加到了我的spring boot应用程序的第一类中,紧随注解之后 @SpringBootApplication

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

稍后,我将使用以下内容从属性文件中获取值,无论该类是我想要使用其值的任何类,并appVersion获取项目版本给我:

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

希望能对某人有所帮助。


如何处理多个pom文件?我想从多个pom文件加载版本。
THM

0

一个与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();
    }

-1

带有maven项目的war文件中的EJB的Java 8变体。在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!");
            }
        });
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.