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アプリでマニフェストアプリを読む方法を知っている人はいますか?
PDATE:いくつかの調査の後、manifest.mfファイルを通常の方法で読み取ると、Spring BootアプリケーションではこれがアクセスされるJarであることに気付きました。
org.springframework.boot.loader.jar.JarFile
Java.lang.Package を使用して、マニフェストからスプリングブートの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)
}
}
これを追加するだけで簡単です
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」
ありがとう、アレクサンダー
ほとんどすべてのjarファイルにはマニフェストファイルが付属しているため、コードはfirstfileを返しますクラスパス内 =。
とにかくマニフェストが必要なのはなぜですか?これはJavaが使用するファイルです。 .properties
ファイルの隣の.class
ファイルなど、必要なカスタム値を他の場所に配置します。
更新2
以下のコメントで述べられているように、not質問では、本当の目標はマニフェストからのバージョン情報です。 Javaはすでに Java.lang.Package
クラスでその情報を提供しています。
マニフェストを見つけられたとしても、自分でマニフェストを読もうとしないでください。
更新1
マニフェストファイルはnotProperties
ファイルであることに注意してください。その構造はそれよりもはるかに複雑です。
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)
ご覧のとおり、Name
とSHA-256-Digest
は複数回出現します。 Properties
クラスはそれを処理できません。これは単なるMap
であり、キーが一意である必要があるためです。
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
、およびサンプルを簡単に拡張して他の属性をロードできます。
Springドキュメントでテストと検索を行った後、マニフェストファイルを読み取る方法を見つけました。
まず、Spring Bootは、リソースのロード方法を変更する独自のClassLoaderを実装します。 getResource()
を呼び出すと、Spring Bootは、クラスパスで使用可能なすべてのJARのリスト内の指定されたリソース名に一致する最初のリソースをロードします。アプリjarは最初の選択肢ではありません。
したがって、次のようなコマンドを発行する場合:
getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");
クラスパスのJarで最初に見つかったMANIFEST.MFファイルが返されます。私の場合、それはJDK jarライブラリから来ました。
ソリューション:
リソース「/META-INF/MANIFEST.MF」を含むアプリによってロードされたすべてのJarのリストを取得し、リソースがアプリケーション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.mfファイルからすべてのデータを返します。
Javaを使用してjarファイルからMANIFEST.MFファイルを読み取る からソリューションの一部を借用しました
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);
...
}
これは、Java -jar
コマンドを使用してアプリを起動した場合にのみ機能し、統合テストを作成した場合は機能しません。