我注意到在 Maven 工件的 JAR 中,project.version 属性包含在两个文件中:
META-INF/maven/${groupId}/${artifactId}/pom.properties
META-INF/maven/${groupId}/${artifactId}/pom.xml
有没有推荐的方法在运行时阅读这个版本?
您不需要访问特定于 Maven 的文件来获取任何给定库/类的版本信息。
您可以简单地使用 getClass().getPackage().getImplementationVersion()
来获取存储在 .jar 文件 MANIFEST.MF
中的版本信息。 幸运的是 Maven 足够聪明 不幸的是,默认情况下 Maven 也不会将正确的信息写入清单!
相反,必须修改 maven-jar-plugin
的 <archive>
配置元素以将 addDefaultImplementationEntries
和 addDefaultSpecificationEntries
设置为 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
或其他 base-pom。
<archive>
元素的详细文档可在 Maven Archive documentation 中找到。
为了跟进上面的答案,对于 .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
(包含在 .war
的 WEB-INF/lib
中)
null
尽管战争文件中的 MANIFEST.MF 包含正确的信息。
<archiveClasses>true</archiveClasses>
- 从那时起它就可以可靠地工作。
这是从 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;
}
我将 maven-assembly-plugin
用于我的 Maven 包装。在 Joachim Sauer's answer 中使用 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>
因为archiver是maven shared components之一,它可以被多个maven构建插件使用,如果引入两个或多个插件,也可能会发生冲突,包括里面的archive
配置。
如果您碰巧使用 Spring Boot,则可以使用 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")));
}
}
我在这里花了一些时间研究这两种主要方法,但它们对我没有用。我正在使用 Netbeans 进行构建,可能还有更多内容。我有一些来自 Maven 3 的错误和警告以及一些构造,但我认为这些很容易纠正。没什么大不了的。
我确实在这篇关于 DZone 的文章中找到了一个看起来可维护且易于实现的答案:
使用 Maven 在属性文件中标记版本号和构建时间
我已经有一个资源/配置子文件夹,我将我的文件命名为:app.properties,以更好地反映我们可能保留在那里的东西(如支持 URL 等)。
唯一需要注意的是,Netbeans 会发出警告,提示 IDE 需要过滤掉。不确定在哪里/如何。在这一点上它没有效果。如果我需要过那座桥,也许有办法解决这个问题。祝你好运。
要使其在 Eclipse 以及 Maven 构建中运行,您应该按照其他回复中的说明添加 addDefaultImplementationEntries
和 addDefaultSpecificationEntries
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 的值。
System.getProperty("user.dir")/pom.xml
。我相当肯定它也适用于其他事情,但可能不适用于 WTP。
.getResource()
或 .getResourceAsStream()
。
在我的 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;
希望对某人有所帮助。
我知道这是一个很晚的答案,但我想根据 this 链接分享我所做的事情:
我将以下代码添加到 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>
这个 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;
}
}
我发现的最优雅的解决方案是来自 J.Chomel 的解决方案:link
不需要任何具有属性的黑客。为避免将来出现链接断开的问题,我将在此处复制它:
YourClass.class.getPackage().getImplementationVersion();
而且(如果你的 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>
...
Bundle-Version
但不定义 Implementation-Version
☹)。
一个简单的解决方案,它与 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;
}
ObjectMapper.class
。
尝试了上面的所有答案,但对我没有任何帮助:
我没有使用弹簧
设法将版本放在清单中,但 someClass.class.getPackage().getImplementationVersion() 返回 null
但是版本被附加到 jar
文件名,所以我能够使用以下方法找到一个 jar 文件:
new File(ClassLoader.getSystemResource("").toURI()).getParentFile();
然后从文件名中提取它。
带有 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!");
}
});
}
}
不定期副业成功案例分享