如何在Spring Boot应用程序中读取我的META-INF / MANIFEST.MF文件?

时间:2015-08-30 06:39:11

标签: java spring-boot manifest.mf

我正在尝试从我的Spring Boot Web应用程序(包含在jar文件中)中读取我的META-INF / MANIFEST.MF文件。

我正在尝试以下代码:

        InputStream is = getClass().getResourceAsStream("/META-INF/MANIFEST.MF");

        Properties prop = new Properties();
        prop.load( is );

但显然Spring Boot中有一些东西加载了一个不同的manifest.mf(而不是我自己位于META-INF文件夹中)。

有谁知道如何在Spring Boot应用程序中阅读我的清单应用程序?

更新:经过一番研究后,我注意到使用通常的方式读取manifest.mf文件,在Spring Boot应用程序中,这是正在访问的Jar

org.springframework.boot.loader.jar.JarFile

7 个答案:

答案 0 :(得分:17)

我使用java.lang.Package从清单中读取spring boot中的Implementation-Version属性。

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

应在Implementation-Version

中配置build.gradle属性
jar {
    baseName = "my-app"
    version =  "0.0.1"
    manifest {
        attributes("Implementation-Version": version)
    }
}

答案 1 :(得分:4)

几乎所有jar文件都附带一个清单文件,因此您的代码将返回 第一个文件,它可以在类路径中找到

为什么你还想要清单呢?它是Java使用的文件。将您需要的任何自定义值放在其他位置,例如.properties文件旁边的.class文件。

更新2

如下面的评论所述,问题中的不是,真正的目标是清单中的版本信息。 Java已经使用java.lang.Package类提供了该信息。

不要尝试自己阅读清单,即使你能找到它。

更新1

请注意,清单文件不是 Properties文件。它的结构要复杂得多。

请参阅JAR File Specification

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

如您所见,NameSHA-256-Digest不止一次出现。 Properties类无法处理,因为它只是一个Map,并且键必须是唯一的。

答案 2 :(得分:4)

这很简单,只需添加此

即可
    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);
    }

为我工作。

注意:

  

getClass()。getClassLoader()很重要

  

" META-INF / MANIFEST.MF"不是" /META-INF/MANIFEST.MF"

由于 亚历山大

答案 3 :(得分:2)

在测试和搜索Spring文档后,我找到了一种读取清单文件的方法。

首先,Spring Boot实现了自己的ClassLoader,它改变了资源的加载方式。当您调用getResource()时,Spring Boot将加载与类路径中可用的所有JAR列表中的给定资源名称匹配的第一个资源,并且您的应用程序jar不是第一选择。

因此,当发出如下命令时:

getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");

返回在类路径的任何Jar中找到的第一个MANIFEST.MF文件。就我而言,它来自JDK jar库。

<强> SOLUTION:

我设法得到包含资源“/META-INF/MANIFEST.MF”的应用程序加载的所有Jars的列表,并检查资源是否来自我的应用程序jar。如果是这样,请阅读其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;
}

此方法将从Manifest对象内的manifest.mf文件返回所有数据。

我从reading MANIFEST.MF file from jar file using JAVA

借用了部分解决方案

答案 4 :(得分:2)

我利用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);
        }
    }

}

这里我们得到CI-Build,您可以轻松扩展示例以加载其他属性。

答案 5 :(得分: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;
}

答案 6 :(得分: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);
        ...
    }

仅当您通过java -jar命令启动应用时,此功能才有效,如果创建集成测试,则无效。