Расскажите о файлах JAR и MANIFEST.MF

Java

В языковом кружке JAVA, кроме изучения языкового уровня и уровня фреймворка, есть некоторые вещи, которые существовали всегда, но им не уделялось должного внимания, потому что все принимают как должное, например, JAR Что?

Когда дело доходит до JAR, первое, что приходит на ум, это зависимости, такие как fastjson.jar, на которые можно ссылаться в проекте как на зависимость, но нельзя выполнить через java -jar, который является неисполняемым JAR. Другой вид, такой как JAR, созданный после того, как наш проект упакован (конечно, это может быть война), мы можем запустить программу через java -jar, который мы называем исполняемым JAR.

Роль JAR можно условно разделить на следующие категории:

  • Для публикации и использования библиотек классов
  • как строительный блок для приложений и расширений
  • Модуль развертывания как компонент, апплет или подключаемый модуль
  • Используется для упаковки вспомогательных ресурсов, связанных с компонентами.

Базовые концепты

Файл JAR — это архивный файл, созданный в формате ZIP с расширением .jar. Пользователи могут создавать или извлекать файлы JAR с помощью команды jar, поставляемой с JDK. Другие инструменты сжатия zip также могут использоваться, но порядок записей в заголовке zip-файла важен при сжатии, поскольку файл МАНИФЕСТА часто должен идти первым. Имена файлов в файлах JAR представляют собой текст Unicode.

Файл JAR (Архив Java, английский: Архив Java) — это формат файла пакета программного обеспечения, обычно используемый для объединения большого количества файлов классов Java, связанных метаданных и файлов ресурсов (текст, изображения и т. д.) в один файл для распространения среди пользователей. прикладное программное обеспечение или библиотека платформы Java.

Выше из Википедии

Формат файла JAR предлагает множество преимуществ и функций, многие из которых не предоставляются традиционными форматами сжатия, такими как ZIP или TAR. Они включают:

  • Безопасность: содержимое файла JAR может быть подписано цифровой подписью. Таким образом, инструмент, распознающий сигнатуру, может выборочно предоставить вам привилегии безопасности программного обеспечения, которых нет ни у одного другого файла, а также определить, был ли код подделан.
  • Сокращение времени загрузки: если апплет упакован в JAR-файл, браузер может загрузить файлы классов апплета и связанные ресурсы в одной HTTP-транзакции, а не открывать новое соединение для каждого файла.
  • Сжатие: формат JAR позволяет сжимать файлы для повышения эффективности хранения.
  • Расширение платформы трансмиссии. Платформа расширений Java (Java Extensions Framework) предоставляет способ добавления функциональных возможностей к основной платформе Java, и эти расширения упакованы в файлы JAR (Java 3D и JavaMail являются примерами расширений, разработанных Sun).
  • Запечатывание пакетов: пакеты, хранящиеся в файлах JAR, могут быть дополнительно запечатаны для повышения согласованности версий и безопасности. Запечатывание пакета означает, что все классы в пакете должны находиться в одном файле JAR.
  • Управление версиями пакетов: файл JAR может содержать данные о содержащихся в нем файлах, например сведения о поставщике и версии.
  • Переносимость. Механизм обработки файлов JAR является стандартной частью основного API платформы Java.

Формат JAR-файла

Вот распакованные примеры двух JAR-файлов соответственно.

Обычный каталог файлов JAR после распаковки

Возьмите fastjson в качестве примера:

.
├── META-INF
│   ├── LICENSE.txt
│   ├── MANIFEST.MF
│   ├── NOTICE.txt
│   ├── maven
│   │   └── com.alibaba
│   │       └── fastjson
│   │           ├── pom.properties
│   │           └── pom.xml
│   └── services
│       ├── javax.ws.rs.ext.MessageBodyReader
│       ├── javax.ws.rs.ext.MessageBodyWriter
│       ├── javax.ws.rs.ext.Providers
│       └── org.glassfish.jersey.internal.spi.AutoDiscoverable
└── com
    └── alibaba
        └── fastjson
            ├── JSON.class
            ├── JSONArray.class
            ├── JSONAware.class
            ├── JSONException.class
            ├── JSONObject.class
            ....省略

Исполняемый jar (в качестве примера возьмем FAT JAR SpringBoot)

Этот jar собран из самой простой демо-версии, скачанной с start.spring.io.

├── BOOT-INF
│   ├── classes
│   │   ├── application.properties
│   │   └── com
│   │       └── example   # 应用的.class 文件目录
│   │           └── demo
│   │               └── DemoApplication.class
│   └── lib # 这里存放的是应用的 Maven 依赖的jar包文件
│       ├── javax.annotation-api-1.3.2.jar
│       ├── jul-to-slf4j-1.7.26.jar
│       ├── log4j-api-2.11.2.jar
│       ├── log4j-to-slf4j-2.11.2.jar
│       ├── logback-classic-1.2.3.jar
│       ├── logback-core-1.2.3.jar
│       ├── slf4j-api-1.7.26.jar
│       ├── snakeyaml-1.23.jar
│       ├── spring-aop-5.1.8.RELEASE.jar
│       ├── spring-beans-5.1.8.RELEASE.jar
│       ├── spring-boot-2.1.6.RELEASE.jar
│       ├── spring-boot-autoconfigure-2.1.6.RELEASE.jar
│       ├── spring-boot-starter-2.1.6.RELEASE.jar
│       ├── spring-boot-starter-logging-2.1.6.RELEASE.jar
│       ├── spring-context-5.1.8.RELEASE.jar
│       ├── spring-core-5.1.8.RELEASE.jar
│       ├── spring-expression-5.1.8.RELEASE.jar
│       └── spring-jcl-5.1.8.RELEASE.jar
├── META-INF
│   ├── MANIFEST.MF
│   └── maven
│       └── com.example
│           └── demo
│               ├── pom.properties
│               └── pom.xml
└── org
    └── springframework
        └── boot
            └── loader #存放的是 Spring boot loader 的 class 文件
                ├── ExecutableArchiveLauncher.class
                ├── JarLauncher.class
                ├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
                ├── LaunchedURLClassLoader.class
                ├── Launcher.class
                ├── MainMethodRunner.class
                ├── PropertiesLauncher$1.class
                ├── PropertiesLauncher$ArchiveEntryFilter.class
                ├── PropertiesLauncher$PrefixMatchingArchiveFilter.class
                ├── PropertiesLauncher.class
                ├── WarLauncher.class
                ├── archive
                │   ├── Archive$Entry.class
                │   ├── ...
                ├── data
                │   ├── RandomAccessData.class
                │   ├── ...
                ├── jar
                │   ├── AsciiBytes.class
                │   ├── ...
                └── util
                    └── SystemPropertyUtils.class

META-INF

Большинство файлов JAR содержат каталог META-INF, в котором хранятся данные конфигурации для пакетов и расширений, такие как информация о безопасности и версии. Платформа Java 2 (стандартная версия [J2SE]) распознает и интерпретирует следующие файлы и каталоги в каталоге META-INF для настройки приложений, расширений и загрузчиков классов:

  • MANIFEST.MF: этот файл манифеста определяет данные, относящиеся к расширениям и пакетам.
  • Файлы, упакованные плагином MAVEN, например:
    • maven
    • services : хранит все файлы конфигурации поставщика услуг
  • Есть и другие, которые не часто встретишь:
    • INDEX.LIST : этот файл создается с помощью новой опции -i инструмента jar и содержит информацию о расположении пакетов, определенных в приложении или расширении. Он является частью реализации JarIndex и используется загрузчиком классов для ускорения процесса загрузки классов.
    • .SF: это файл подписи для файла JAR.
    • .DSA: файл блока подписи, связанный с файлом подписи, в котором хранится общедоступная подпись, используемая для подписания файла JAR.
    • LICENSE.txt: информация о сертификате
    • NOTICE.txt: Информация об уведомлении

исполняемый JAR-файл

Самая непосредственная разница между исполняемым JAR и обычным JAR заключается в том, можно ли его выполнить с помощью java -jar.

Исполняемый файл jar — это автономное приложение Java, хранящееся в специально сконфигурированном файле JAR, который может быть запущен JVM напрямую без предварительного извлечения файлов или установки пути к классам. Чтобы запустить приложение, хранящееся в неисполняемом JAR-файле, вы должны добавить его в свой путь к классам и вызвать основной класс приложения по имени. Но с исполняемым JAR-файлом мы можем запустить приложение, не извлекая его и не зная основной точки входа. Исполняемые файлы JAR облегчают распространение и выполнение приложений Java.

Исполняемый файл JAR должен ссылаться на все другие зависимые файлы JAR, которые ему нужны, через заголовок файла манифеста. Если используется параметр -jar, переменная среды CLASSPATH и все пути к классам, указанные в командной строке, игнорируются JVM.

Файл MANIFEST.MF

Когда мы используем команду JAR для упаковки пакета, в корневом каталоге будет создан каталог META-INF.В этом каталоге будут некоторые описания информации о пакете JAR, и обязательно будет файл MANIFEST.MF, который содержит такую ​​информацию, как версия, создатель и путь поиска класса JAR-пакета.

  • Файл MANIFEST.MF в банке FASTJSON

    Manifest-Version: 1.0              # 用来定义manifest文件的版本
    Archiver-Version: Plexus Archiver  # 详见 http://codehaus-plexus.github.io/plexus-archiver/
    Built-By: wenshao                  # 构建者
    Created-By: Apache Maven 3.5.0  #  # 声明该文件的生成者,一般该属性是由 jar 命令行工具生成的
    Build-Jdk: 1.8.0_162               # 基于构建的 JDK 版本
    
  • Файл MANIFEST.MF для демонстрации SpringBoot

    Manifest-Version: 1.0
    Implementation-Title: demo                     # 定义了扩展实现的标题
    Implementation-Version: 0.0.1-SNAPSHOT         # 定义扩展实现的版本
    Start-Class: com.example.demo.DemoApplication  # 启动类
    Spring-Boot-Classes: BOOT-INF/classes/         # 编译之后的 class 文件目录
    Spring-Boot-Lib: BOOT-INF/lib/                 # 当前工程依赖的 jar 包目录
    Build-Jdk-Spec: 1.8                            # 指定的 JDK 版本
    Spring-Boot-Version: 2.1.6.RELEASE             # SpringBoot 版本
    Created-By: Maven Archiver 3.4.0             
    Main-Class: org.springframework.boot.loader.JarLauncher  # Main 函数
    

На платформе Java файл МАНИФЕСТ представляет собой специальный файл, содержащийся в архиве JAR, который используется для определения расширений или данных, связанных с упаковкой файлов.

Файл MANIFEST действует как файл метаданных, который содержит k-v пар данных в разных разделах.

Если файл JAR рассматривается как исполняемый файл, файл МАНИФЕСТА в нем должен указывать файл основного класса программы, как показано в файле МАНИФЕСТА в банке демонстрации SpringBoot в приведенном выше случае.

МАНИФЕСТ роль

Из информации, представленной в файле MANIFEST, вы, вероятно, можете понять его основную роль.

  • Описание основной информации пакета JAR
  • Main-Class указывает запись программы, чтобы программу можно было запустить напрямую с помощью java -jar xxx.jar
  • Class-Path указывает зависимости пакета jar, и загрузчик классов будет искать классы по этому пути.

Получить MANIFEST.MF

JDK предоставляет инструмент для получения информации из файла MANIFEST.MF в пакете jar, который можно получить с помощью библиотеки классов java.util.jar.

JarFile jar = new JarFile(new File("/Users/glmapper/Documents/test/demo/target/demo-0.0.1-SNAPSHOT.jar"));
Manifest manifest = jar.getManifest();
Attributes mainAttributes = manifest.getMainAttributes();
for(Map.Entry<Object, Object> attrEntry : mainAttributes.entrySet()){
    System.out.println("main\t"+attrEntry.getKey()+":"+attrEntry.getValue());
}
Map<String, Attributes> entries = manifest.getEntries();
for(Map.Entry<String, Attributes> entry : entries.entrySet()) {
    Attributes values = entry.getValue();
    for (Map.Entry<Object, Object> attrEntry : values.entrySet()) {
        System.out.println(attrEntry.getKey() + ":" + attrEntry.getValue());
    }
}

Результат выполнения:

main	Implementation-Title:demo
main	Implementation-Version:0.0.1-SNAPSHOT
main	Start-Class:com.example.demo.DemoApplication
main	Spring-Boot-Classes:BOOT-INF/classes/
main	Spring-Boot-Lib:BOOT-INF/lib/
main	Build-Jdk-Spec:1.8
main	Spring-Boot-Version:2.1.6.RELEASE
main	Created-By:Maven Archiver 3.4.0
main	Manifest-Version:1.0
main	Main-Class:org.springframework.boot.loader.JarLauncher

Определение файла Jar и манифеста в java

Ниже приводится определение JarFile.Из кода видно, что Jar, который мы представили ранее, предназначен для создания архивного файла в формате ZIP, поскольку он является подклассом ZipFile.

public class JarFile extends ZipFile {
    private SoftReference<Manifest> manRef;
    private JarEntry manEntry;
    private JarVerifier jv;
    private boolean jvInitialized;
    private boolean verify;
    //指示是否存在Class-Path属性(仅当hasCheckedSpecialAttributes为true时才有效)
    private boolean hasClassPathAttribute;
    // 如果清单检查特殊属性,则为 true
    private volatile boolean hasCheckedSpecialAttributes;
    // 在SharedSecrets中设置JavaUtilJarAccess
    static {
        SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
    }
    /**
     * The JAR manifest file name.(JAR清单文件名)
     */
    public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
    // 省略其他
}

Ниже приведено определение класса Manifest, используемого для описания файла манифеста JAR. Из его свойств также хорошо видно, что он хранит данные пары ключ-значение K-V.

public class Manifest implements Cloneable {
    // manifest main attributes
    private Attributes attr = new Attributes();
    // manifest entries
    private Map<String, Attributes> entries = new HashMap<>();
    // 省略其他
}

резюме

Формат JAR — это гораздо больше, чем просто сжатый формат, он имеет множество функций, повышающих эффективность, безопасность и организацию приложений Java. Поскольку эти возможности уже встроены в базовую платформу, включая компилятор и загрузчик классов, разработчики могут воспользоваться преимуществами формата файла JAR для упрощения и улучшения процесса разработки и развертывания.

Приложение: Обычное использование инструмента jar

Функции Заказ
Создайте файл JAR с одним файлом jar cf jar-file input-file...
Создайте файл JAR с каталогом jar cf jar-file dir-name
Создайте несжатый файл JAR jar cf0 jar-file dir-name
Обновить файл JAR jar uf jar-file input-file...
Просмотр содержимого файла JAR jar tf jar-file
Извлечь содержимое файла JAR jar xf jar-file
Извлечь определенные файлы из файла JAR jar xf jar-file archived-file...
Запустите приложение, упакованное в виде исполняемого файла JAR. java -jar app.jar

Ссылаться на