在运行时获取 Maven 工件版本

我注意到,在 Maven 工件的 JAR 中,project.version 属性包含在两个文件中:

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

是否有在运行时读取此版本的推荐方法?

162072 次浏览

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

您可以简单地使用 getClass().getPackage().getImplementationVersion()来获取存储在。Jar-files MANIFEST.MF.幸运的是 Maven 足够聪明不幸的是,Maven 在默认情况下没有将正确的信息写入到清单中!

相反,我们必须修改 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>

理想情况下,这种配置应该放入公司 pom或另一个基点。

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

为了跟踪上面的答案,对于 .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>

这将版本信息添加到项目的 .jar中的 MANIFEST.MF(包括在 .warWEB-INF/lib中)

下面是一个从 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;
}

我花了一些时间在这里的两个主要方法,他们不适合我。我正在使用 Netbeans 进行构建,可能还有更多内容。我在 Maven3中使用了一些结构,发现了一些错误和警告,但我认为这些错误和警告很容易纠正。没什么大不了的。

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

  • 加盖版本号和 Maven 属性文件中的构建时间

我已经有了一个 resources/config 子文件夹,并将我的文件命名为: app.properties,以更好地反映我们可能保留在其中的内容(比如支持 URL 等)。

唯一的警告是 Netbeans 会发出警告说 IDE 需要过滤掉。不知道在哪里,怎么做。在这一点上没有效果。如果我需要过那座桥的话,也许还有别的办法。祝你好运。

为了在 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;
}

如果您的 Java 构建将目标类放在“ target/classes”之外的某个地方,那么您可能需要调整 segmentsToRemove 的值。

我使用 maven-assembly-plugin作为我的 Maven 包装。在 Joachim Sauer 的回答中使用 Apache Maven 归档器也可以起作用:

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

由于 Archiever 是 Maven 共享组件中的一个,所以它可以被多个 maven 构建插件使用,如果引入两个或更多的插件(包括内部的 archive配置) ,这些插件也可能发生冲突。

在 war 文件中用于 EJB 的 Java 8变体和 maven 项目。在 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!");
}
});
}
}

在我的春季启动应用程序中,来自已接受答案的解决方案一直有效,直到我最近将 jdk 更新到12版。其他的答案也都试过了,但都没有成功。

此时,我将下面的代码行添加到我的 Spring 启动应用程序的第一个类中,就在注释 @SpringBootApplication之后

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

后来,我使用下面的方法从属性文件中获取我想要使用它的值的类中的值,然后 appVersion将项目版本发送给我:

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

希望这对某人有帮助。

一个简单的解决方案是 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();
}

下面是一个没有 Optional<>的版本,如果不存在,它只返回 null(用于快速调试/转储) :

    private static 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();
return attributes.getValue("Bundle-Version");
}
} catch (Exception e) {
// ignore
}
return null;
}

如果您碰巧使用 SpringBoot,您可以使用 BuildProperties类。

以 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")));
}
}

我知道这是一个非常晚的答案,但我想分享我所做的每 这个链接:

我将下面的代码添加到 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>

为了获得模型属性的版本,通知控制器:

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

我找到的最优雅的解决方案是来自 J.Chomel: 链接的那个

不需要对属性进行任何修改。为了避免将来出现链接中断的问题,我将在这里复制它:

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

而且(如果在 jar/war 中还没有 Manifest 文件,对我来说 Intellij Ideas 的 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>
...

我尝试了上面所有的答案,但没有一个对我有用:

  • 我没有用 Spring
  • 设法将 Version 放在清单中,但是 someClass.class.getPackage().getImplementationVersion()返回 null

然而,版本被附加到 jar文件名,所以我能够找到一个 jar 文件使用:

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

然后从文件名中提取出来。