[英]How to read my META-INF/MANIFEST.MF file in a Spring Boot app?
I'm trying to read my META-INF/MANIFEST.MF file from my Spring Boot web app (contained in a jar file). 我正在尝试从我的Spring Boot Web应用程序(包含在jar文件中)中读取我的META-INF / MANIFEST.MF文件。
I'm trying the following code: 我正在尝试以下代码:
InputStream is = getClass().getResourceAsStream("/META-INF/MANIFEST.MF");
Properties prop = new Properties();
prop.load( is );
But apparently there is something behind the scenes in Spring Boot that a different manifest.mf is loaded (and not my own located in META-INF folder). 但显然Spring Boot中有一些东西可以加载一个不同的manifest.mf(而不是我自己位于META-INF文件夹中)。
Does anyone know how I can read my manifest app in my Spring Boot app? 有谁知道如何在Spring Boot应用程序中阅读我的清单应用程序?
UPDATE: After some research I noticed that using the usual way to read a manifest.mf file, in a Spring Boot application this is the Jar that is being accessed 更新:经过一些研究后,我注意到使用通常的方式读取manifest.mf文件,在Spring Boot应用程序中,这是正在访问的Jar
org.springframework.boot.loader.jar.JarFile
I use java.lang.Package to read the Implementation-Version
attribute in spring boot from the manifest. 我使用java.lang.Package从清单中读取spring boot中的
Implementation-Version
属性。
String version = Application.class.getPackage().getImplementationVersion();
The Implementation-Version
attribute should be configured in build.gradle
应在
build.gradle
配置Implementation-Version
属性
jar {
baseName = "my-app"
version = "0.0.1"
manifest {
attributes("Implementation-Version": version)
}
}
It's simple just add this 这很简单就是添加它
InputStream is = this.getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
Properties prop = new Properties();
try {
prop.load( is );
} catch (IOException ex) {
Logger.getLogger(IndexController.class.getName()).log(Level.SEVERE, null, ex);
}
Working for me. 为我工作
Note: 注意:
getClass().getClassLoader() is important
getClass()。getClassLoader()很重要
and 和
"META-INF/MANIFEST.MF" not "/META-INF/MANIFEST.MF"
“META-INF / MANIFEST.MF”不是“/ META-INF / MANIFEST.MF”
Thanks Aleksandar 谢谢Aleksandar
Pretty much all jar files come with a manifest file, so your code is returning the first file it can find in the classpath . 几乎所有jar文件都带有清单文件,因此您的代码将返回它可以在类路径中找到的第一个文件 。
Why would you want the manifest anyway? 为什么你还想要清单呢? It's a file used by Java.
它是Java使用的文件。 Put any custom values you need somewhere else, like in a
.properties
file next to you .class
file. 将您需要的任何自定义值放在其他位置,例如在
.class
文件旁边的.properties
文件中。
Update 2 更新2
As mentioned in a comment below, not in the question, the real goal is the version information from the manifest. 如下面的评论所述, 不在问题中,真正的目标是清单中的版本信息。 Java already supplies that information with the
java.lang.Package
class. Java已经通过
java.lang.Package
类提供了该信息。
Don't try to read the manifest yourself, even if you could find it. 即使你能找到它,也不要试图自己阅读清单。
Update 1 更新1
Note that the manifest file is not a Properties
file. 请注意,清单文件不是
Properties
文件。 It's structure is much more complex than that. 它的结构要复杂得多。
See the example in the java documentation of the JAR File Specification . 请参阅JAR文件规范的Java文档中的示例。
Manifest-Version: 1.0
Created-By: 1.7.0 (Sun Microsystems Inc.)
Name: common/class1.class
SHA-256-Digest: (base64 representation of SHA-256 digest)
Name: common/class2.class
SHA1-Digest: (base64 representation of SHA1 digest)
SHA-256-Digest: (base64 representation of SHA-256 digest)
As you can see, Name
and SHA-256-Digest
occurs more than once. 如您所见,
Name
和SHA-256-Digest
不止一次出现。 The Properties
class cannot handle that, since it's just a Map
and the keys have to be unique. Properties
类无法处理,因为它只是一个Map
,键必须是唯一的。
After testing and searching on Spring docs, I found a way to read the manifest file. 在测试和搜索Spring文档后,我找到了一种方法来读取清单文件。
First off, Spring Boot implements its own ClassLoader that changes the way how resources are loaded. 首先,Spring Boot实现了自己的ClassLoader,它改变了资源的加载方式。 When you call
getResource()
, Spring Boot will load the first resource that matches the given resource name in the list of all JARs available in the class path, and your app jar is not the first choice. 当您调用
getResource()
,Spring Boot将加载与类路径中可用的所有JAR列表中的给定资源名称匹配的第一个资源,并且您的应用程序jar不是第一选择。
So, when issuing a command like: 因此,在发出如下命令时:
getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");
The first MANIFEST.MF file found in any Jar of the Class Path is returned. 返回在类路径的任何Jar中找到的第一个MANIFEST.MF文件。 In my case, it came from a JDK jar library.
就我而言,它来自JDK jar库。
SOLUTION: 解:
I managed to get a list of all Jars loaded by the app that contains the resource "/META-INF/MANIFEST.MF" and checked if the resource came from my application jar. 我设法得到包含资源“/META-INF/MANIFEST.MF”的应用程序加载的所有Jars的列表,并检查资源是否来自我的应用程序jar。 If so, read its MANIFEST.MF file and return to the app, like that:
如果是这样,请阅读其MANIFEST.MF文件并返回到应用程序,如下所示:
private Manifest getManifest() {
// get the full name of the application manifest file
String appManifestFileName = this.getClass().getProtectionDomain().getCodeSource().getLocation().toString() + JarFile.MANIFEST_NAME;
Enumeration resEnum;
try {
// get a list of all manifest files found in the jars loaded by the app
resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
while (resEnum.hasMoreElements()) {
try {
URL url = (URL)resEnum.nextElement();
// is the app manifest file?
if (url.toString().equals(appManifestFileName)) {
// open the manifest
InputStream is = url.openStream();
if (is != null) {
// read the manifest and return it to the application
Manifest manifest = new Manifest(is);
return manifest;
}
}
}
catch (Exception e) {
// Silently ignore wrong manifests on classpath?
}
}
} catch (IOException e1) {
// Silently ignore wrong manifests on classpath?
}
return null;
}
This method will return all data from a manifest.mf file inside a Manifest object. 此方法将从Manifest对象内的manifest.mf文件返回所有数据。
I borrowed part of the solution from reading MANIFEST.MF file from jar file using JAVA 我从使用JAVA的jar文件中读取MANIFEST.MF文件中借用了部分解决方案
I have it exploiting Spring's resource resolution: 我有它利用Spring的资源解析:
@Service
public class ManifestService {
protected String ciBuild;
public String getCiBuild() { return ciBuild; }
@Value("${manifest.basename:/META-INF/MANIFEST.MF}")
protected void setManifestBasename(Resource resource) {
if (!resource.exists()) return;
try (final InputStream stream = resource.getInputStream()) {
final Manifest manifest = new Manifest(stream);
ciBuild = manifest.getMainAttributes().getValue("CI-Build");
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Here we're getting CI-Build
, and you can easily extend the example to load other attributes. 在这里,我们将获得
CI-Build
,您可以轻松扩展示例以加载其他属性。
public Properties readManifest() throws IOException {
Object inputStream = this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
JarInputStream jarInputStream = new JarInputStream((InputStream) inputStream);
Manifest manifest = jarInputStream.getManifest();
Attributes attributes = manifest.getMainAttributes();
Properties properties = new Properties();
properties.putAll(attributes);
return properties;
}
try {
final JarFile jarFile = (JarFile) this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
final Manifest manifest = jarFile.getManifest();
final Map<Object, Object> manifestProps = manifest.getMainAttributes().entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
...
} catch (final IOException e) {
LOG.error("Unable to read MANIFEST.MF", e);
...
}
This will work only if you launch your app via java -jar
command, it won't work if one create an integration test. 这仅在您通过
java -jar
命令启动应用程序时才有效,如果创建集成测试,它将无法工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.