Как прочитать мой файл META-INF/MANIFEST.MF в приложении Spring Boot?
Я пытаюсь прочитать мой файл META-INF/MANIFEST.MF из моего веб-приложения Spring Boot (содержащегося в файле jar).
Я пробую следующий код:
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
Ответы
Ответ 1
Я использую 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)
}
}
Ответ 2
Практически все файлы jar имеют файл манифеста, поэтому ваш код возвращает первый файл, который он может найти в пути к классам.
Почему вы хотите манифест в любом случае? Это файл, используемый Java. Поместите любые пользовательские значения, которые вам нужны в другом месте, например, в файле .properties
рядом с вами .class
.
Обновление 2
Как упоминалось в комментарии ниже, а не в вопросе, реальной целью является информация о версии из манифеста. Java уже предоставляет эту информацию в классе java.lang.Package
.
Не пытайтесь читать манифест самостоятельно, даже если бы вы его нашли.
Обновление 1
Обратите внимание, что файл манифеста не является файлом Properties
. Эта структура намного сложнее.
См. Пример в java-документации JAR File Specification.
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
и ключи должны быть уникальными.
Ответ 3
Просто добавьте это
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"
Спасибо Aleksandar
Ответ 4
После тестирования и поиска в документах Spring я нашел способ прочитать файл манифеста.
Во-первых, Spring Boot реализует свой собственный ClassLoader, который изменяет способ загрузки ресурсов. Когда вы вызываете getResource()
, Spring Boot загружает первый ресурс, который соответствует указанному названию ресурса в списке всех JAR-ов, доступных в пути к классу, и ваш банкомат приложения не является первым выбором.
Таким образом, при выдаче команды, например:
getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");
Возвращается первый файл MANIFEST.MF, найденный в любом Jar пути класса. В моем случае это произошло из библиотеки JDK jar.
РЕШЕНИЕ:
Мне удалось получить список всех Jars, загруженных приложением, которое содержит ресурс "/META-INF/MANIFEST.MF", и проверил, появился ли ресурс из моего приложения. Если да, прочитайте его файл 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 внутри объекта манифеста.
Я заимствовал часть решения от чтения файла MANIFEST.MF из файла jar с помощью JAVA
Ответ 5
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
Я использую 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
, и вы можете легко расширить этот пример, чтобы загрузить другие атрибуты.
Ответ 7
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
, это не сработает, если вы создадите тест интеграции.