如何阅读我的META-INF/MANIFEST。在Spring引导应用程序中的MF文件?

时间:2021-10-31 06:29:02

I'm trying to read my META-INF/MANIFEST.MF file from my Spring Boot web app (contained in a jar file).

我在阅读我的META-INF/MANIFEST。来自我的Spring Boot web应用程序的MF文件(包含在jar文件中)。

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的幕后有一些东西是一个不同的清单。mf被加载(而不是我自己位于META-INF文件夹中的)。

Does anyone know how I can read my manifest app in my Spring Boot app?

有人知道我如何在我的Spring Boot应用中读取我的manifest应用吗?

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

更新:在一些研究之后,我注意到使用通常的方式来阅读清单。mf文件,在Spring引导应用程序中,这是正在被访问的Jar

org.springframework.boot.loader.jar.JarFile

7 个解决方案

#1


13  

I use java.lang.Package to read the Implementation-Version attribute in spring boot from the manifest.

我使用. lang。从清单中读取spring引导中的实现版本属性的包。

String version = Application.class.getPackage().getImplementationVersion();

The Implementation-Version attribute should be configured in build.gradle

实现版本属性应该在build.gradle中配置

jar {
    baseName = "my-app"
    version =  "0.0.1"
    manifest {
        attributes("Implementation-Version": version)
    }
}

#2


4  

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

.getClassLoader getClass()()是很重要的

and

"META-INF/MANIFEST.MF" not "/META-INF/MANIFEST.MF"

“meta - inf / MANIFEST。MF / meta - inf / manifest . MF“不

Thanks Aleksandar

由于亚历山大

#3


3  

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. Put any custom values you need somewhere else, like in a .properties file next to you .class file.

你为什么要舱单?它是Java使用的文件。将您需要的任何自定义值放在其他地方,比如在.properties文件中,放在.class文件旁边。

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提供了这些信息。包类。

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. 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. The Properties class cannot handle that, since it's just a Map and the keys have to be unique.

正如您所看到的,名称和sha -256摘要发生不止一次。属性类不能处理这个,因为它只是一个映射,而且键必须是唯一的。

#4


1  

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

#5


1  

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

首先,Spring Boot实现了自己的类加载器,该加载器可以改变资源的加载方式。当您调用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. In my case, it came from a JDK jar library.

第一个清单。在类路径的任何Jar中找到的MF文件将被返回。在我的例子中,它来自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. If so, read its MANIFEST.MF file and return to the app, like that:

我设法获得了一个包含资源“/META-INF/MANIFEST”的应用程序加载的所有jar的列表。并检查资源是否来自我的应用程序jar。如果是,请阅读它的清单。MF文件返回到app,如:

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.

此方法将返回清单中的所有数据。清单对象中的mf文件。

I borrowed part of the solution from reading MANIFEST.MF file from jar file using JAVA

我从阅读清单中借用了部分解决方案。MF文件从jar文件使用JAVA

#6


1  

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,您可以轻松地扩展示例以加载其他属性。

#7


0  

    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命令启动应用程序时有效,如果创建了集成测试,就不能工作。

#1


13  

I use java.lang.Package to read the Implementation-Version attribute in spring boot from the manifest.

我使用. lang。从清单中读取spring引导中的实现版本属性的包。

String version = Application.class.getPackage().getImplementationVersion();

The Implementation-Version attribute should be configured in build.gradle

实现版本属性应该在build.gradle中配置

jar {
    baseName = "my-app"
    version =  "0.0.1"
    manifest {
        attributes("Implementation-Version": version)
    }
}

#2


4  

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

.getClassLoader getClass()()是很重要的

and

"META-INF/MANIFEST.MF" not "/META-INF/MANIFEST.MF"

“meta - inf / MANIFEST。MF / meta - inf / manifest . MF“不

Thanks Aleksandar

由于亚历山大

#3


3  

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. Put any custom values you need somewhere else, like in a .properties file next to you .class file.

你为什么要舱单?它是Java使用的文件。将您需要的任何自定义值放在其他地方,比如在.properties文件中,放在.class文件旁边。

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提供了这些信息。包类。

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. 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. The Properties class cannot handle that, since it's just a Map and the keys have to be unique.

正如您所看到的,名称和sha -256摘要发生不止一次。属性类不能处理这个,因为它只是一个映射,而且键必须是唯一的。

#4


1  

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

#5


1  

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

首先,Spring Boot实现了自己的类加载器,该加载器可以改变资源的加载方式。当您调用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. In my case, it came from a JDK jar library.

第一个清单。在类路径的任何Jar中找到的MF文件将被返回。在我的例子中,它来自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. If so, read its MANIFEST.MF file and return to the app, like that:

我设法获得了一个包含资源“/META-INF/MANIFEST”的应用程序加载的所有jar的列表。并检查资源是否来自我的应用程序jar。如果是,请阅读它的清单。MF文件返回到app,如:

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.

此方法将返回清单中的所有数据。清单对象中的mf文件。

I borrowed part of the solution from reading MANIFEST.MF file from jar file using JAVA

我从阅读清单中借用了部分解决方案。MF文件从jar文件使用JAVA

#6


1  

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,您可以轻松地扩展示例以加载其他属性。

#7


0  

    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命令启动应用程序时有效,如果创建了集成测试,就不能工作。