在 JAVA 語言這個圈子裏面摸爬滾打,除了對於語言層面和框架層面的學習以外,有一些東西它一直存在,可是確沒有對它們有足夠的重視,由於都以爲它是理所固然,好比 JAR 是個什麼?html
提到 JAR,最早可能想到的就是依賴,好比 fastjson.jar ,它能夠做爲依賴在項目中來引用,可是不能經過 java -jar 來執行,這種就是非可執行的 JAR。另一種,好比咱們項目打包以後生成的 JAR (固然也多是 war),咱們能夠經過 java -jar 來運行程序,咱們把它稱之爲可執行的 JAR。java
JAR 做用大致能夠分爲如下幾種:git
JAR 文件是一種歸檔文件,以 ZIP 格式構建,以 .jar 爲文件擴展名。用戶可使用 JDK 自帶的 jar 命令建立或提取 JAR 文件。也可使用其餘 zip 壓縮工具,不過壓縮時 zip 文件頭裏的條目順序很重要,由於 MANIFEST 文件常需放在首位。JAR 文件內的文件名是 Unicode 文本。github
JAR 文件(Java 歸檔,英語:Java Archive)是一種軟件包文件格式,一般用於聚合大量的 Java 類文件、相關的元數據和資源(文本、圖片等)文件到一個文件,以便分發 Java 平臺應用軟件或庫。spring
以上來自維基百科express
JAR 文件格式提供了許多優點和功能,其中不少是傳統的壓縮格式如 ZIP 或者 TAR 所沒有提供的。它們包括:json
這裏分別給出兩個 JAR 的解壓以後的示例api
以 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 是從 start.spring.io 上下載下來的一個最簡單的 demo 打包來的安全
├── 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
複製代碼
大多數 JAR 文件包含一個 META-INF 目錄,它用於存儲包和擴展的配置數據,如安全性和版本信息。Java 2 平臺(標準版【J2SE】)識別並解釋 META-INF 目錄中的下述文件和目錄,以便配置應用程序、擴展和類裝載器:
能夠執行的 JAR 與 普通的 JAR 最直接的區別就是可否經過 java -jar 來執行。
一個 可執行的 jar文件是一個自包含的 Java 應用程序,它存儲在特別配置的 JAR 文件中,能夠由 JVM 直接執行它而無需事先提取文件或者設置類路徑。要運行存儲在非可執行的 JAR 中的應用程序,必須將它加入到您的類路徑中,並用名字調用應用程序的主類。可是使用可執行的 JAR 文件,咱們能夠不用提取它或者知道主要入口點就能夠運行一個應用程序。可執行 JAR 有助於方便發佈和執行 Java 應用程序
一個可執行的 JAR 必須經過 menifest 文件的頭引用它所須要的全部其餘從屬 JAR。若是使用了 -jar選項,那麼環境變量 CLASSPATH 和在命令行中指定的全部類路徑都被 JVM 所忽略。
當咱們用 JAR 命令打完包後,會在根目錄下面建立 META-INF 目錄,該目錄下面會有一些對該 JAR 包信息的描述,其中確定會有一個 MANIFEST.MF 文件,該文件包含了該 JAR 包的版本、建立人和類搜索路徑等信息。
FASTJSON jar 中的 MANIFEST.MF 文件
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 版本
複製代碼
SpringBoot demo 的 MANIFEST.MF 文件
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 平臺中, MANIFEST 文件是 JAR 歸檔中所包含的特殊文件,MANIFEST 文件被用來定義擴展或文件打包相關數據。
MANIFEST 文件做爲一個元數據文件,它包含了不一樣部分中的 k-v 對數據。
若是一個 JAR 文件被看成可執行文件,則其中的 MANIFEST 文件須要指出該程序的主類文件,如上面案例中的 SpringBoot demo 的那個 jar 中的MANIFEST 文件所示
從 MANIFEST 文件中提供的信息大概能夠了解到其基本做用
JDK 中提供了能夠獲取 jar 包中 MANIFEST.MF 文件信息的工具,能夠經過 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
複製代碼
下面爲 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 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 |