java程序員天天不是在建立jar包就是在建立jar包的路上,而且各類依賴引用都是以jar包的形式展現的。可是隨着現代IDE的出現,我想不少程序員已經基本上不多直接和jar包打交道了。java
換句話說,他們已經不認識jar包了。程序員
那麼jar包究竟是什麼呢?它有哪些小祕密呢?一塊兒來看一下吧。算法
jar包實際上是一種zip格式的文件,因此說你其實是可使用zip相關的命令來對jar包進行建立或者解壓縮操做。apache
不一樣的是jar包中多了一個META-INF文件夾。經過這個文件夾,jar包能夠執行更多的操做。安全
JDK也自帶了一個jar命令,經過jar命令咱們能夠實現建立,更新jar包的操做,下圖是JDK8中jar命令的說明:ide
由於JDK9以後引入了模塊化的概念,因此JDK9以後jar命令有了比較大的變化:模塊化
咱們看一下JDK14中的jar命令的用法:ui
這裏主要不是講jar命令,因此咱們不具體展開。spa
jar包和zip包最大的區別就在於jar包中包含了META-INF目錄(不是必須的),咱們看一個比較經常使用的lombok.jar包的結構是怎麼樣的:code
這個版本比較新,因此它使用的是最新的JPMS的寫法,你們能夠看到在jar包的根目錄下面有一個module-info.class文件,表示這個jar包使用的是模塊化。
而後再看一下META-INF目錄,裏面有一個MANIFEST.MF文件:
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.1 Created-By: 14.3-b01-101 (Apple Inc.) Premain-Class: lombok.launch.Agent Agent-Class: lombok.launch.Agent Can-Redefine-Classes: true Main-Class: lombok.launch.Main Lombok-Version: 1.18.10
MANIFEST.MF主要用來定義package相關的數據,這裏咱們能夠看到lombok的MANIFEST.MF文件定義了manifest的版本號,建立時間,版本號和幾個類型的class。
services文件夾裏面存放的能夠對外提供的服務。
這裏列出的文件並不全,實際上還有下面幾種文件:
可使用 -i在生成jar包的時候自動建立,是class的index文件,主要用來加速class加載。
JAR包的簽名文件。
與具備相同基本文件名的簽名文件關聯的簽名塊文件。 該文件存儲相應簽名文件的數字簽名。
主要爲使用多版本的特性準備的,裏面存儲的是不一樣版本的class和資源。
好比下面命令建立了多個版本發行的jar包,而且將一些文件放在 META-INF/versions/9 目錄中。
jar --create --file mr.jar -C foo classes --release 9 -C foo9 classes
假如咱們使用的是JDK9以後的JPMS模塊化,那麼就會生成這麼一個module-info.class。這個文件主要是描述模塊和外部模塊直接的關係。
看一下lombok的例子:
module lombok { requires java.compiler; requires java.instrument; requires jdk.unsupported; requires static org.mapstruct.processor; exports lombok; exports lombok.experimental; exports lombok.extern.apachecommons; exports lombok.extern.java; exports lombok.extern.jbosslog; exports lombok.extern.log4j; exports lombok.extern.slf4j; exports lombok.extern.flogger; provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider$AnnotationProcessor; provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider$AstModificationNotifier; }
這裏面咱們定義了依賴的類和service providers,同時也定義了對外提供的類。
在JDK9以後,存在兩種path,一種是以前的class path,一種是module path。當 modular JAR被部署在module path中的時候,它就是一個modular JAR。當他被部署在class path中的時候,就是一個non-modular JAR。
一樣的,若是是一個non-modular JAR被定義在module path中,那麼這個non-modular JAR就自動被轉換成了一個automatic module。
若是jar包在MANIFEST.MF中定義了Automatic-Module-Name,那麼module名字就是這個值,不然會從JAR的名字來定義這個module。
automatic module主要是爲了向下兼容而產生的。
關於JPMS的更多信息能夠參考我以前寫的文章:JDK9的新特性:JPMS模塊化.
versions主要和 multi-release JAR一塊兒使用的:
Multi-Release: true
所謂multi-release JAR就是說一個jar包能夠支持不一樣版本的JDK。咱們能夠根據須要指定不一樣版本的JDK所依賴的class文件或者屬性文件。這個特性在咱們進行JDK升級的時候仍是頗有幫助的。
通常來講,目錄結構是這樣的:META-INF/versions/N
其中N表示的是JDK的主要發行版本,好比9,10,11等。
類加載器會先去META-INF/versions/N目錄中加載所須要的class,而後會去其餘的低版本的META-INF/versions/N目錄中加載所須要的class,最後纔會在META-INF/的根目錄加載其餘的class文件。
MANIFEST.MF中存放的是key:value格式的配置信息,這些配置信息又能夠分紅兩部分,第一部分是main-section信息,第二部分是individual-section。
咱們舉個簡單的例子:
Manifest-Version: 1.0 Created-By: 1.8 (Oracle Inc.) Sealed: true Name: foo/bar/ Sealed: false
其中
Manifest-Version: 1.0 Created-By: 1.8 (Oracle Inc.) Sealed: true
就是main-section信息,咱們用一張圖來看一下main-section的信息有哪些:
在main-section信息下發能夠接一個Name: Value,表示開啓獨立的針對於具體entry的屬性(Per-Entry Attributes)配置:
Name: foo/bar/ Sealed: false
好比上面的屬性是專門針對於包foo/bar/的,而且設置其Sealed屬性爲false。
Per-Entry Attributes除了 package versioning 和 sealing信息外,還能夠定義Content-Type,Java-Bean,x-Digest-y和Magic屬性。
JAR包能夠經過使用jarsigner來對其進行簽名。和簽名相關的文件是:
簽名事後的jar跟原來的jar其實並無什麼不一樣,只不過在META-INF/文件夾中多出了兩個文件,一個是簽名文件,一個是簽名block文件。
簽名文件是以.SF結尾的,這個文件和MANIFEST.MF很相似,能夠指定Signature-Version和Created-By。
除此以外,還能夠指定和安全相關的屬性:
這兩個屬性主要用來作驗證簽名用的。
舉個例子:
若是咱們的manifest是下面這樣的:
Manifest-Version: 1.0 Created-By: 1.8.0 (Oracle 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)
那麼相應的簽名文件應該是這樣的:
Signature-Version: 1.0 SHA-256-Digest-Manifest: (base64 representation of SHA-256 digest) SHA-256-Digest-Manifest-Main-Attributes: (base64 representation of SHA-256 digest) Name: common/class1.class SHA-256-Digest: (base64 representation of SHA-256 digest) Name: common/class2.class SHA-256-Digest: (base64 representation of SHA-256 digest)
若是再對.SF文件進行摘要,那麼就會獲得簽名文件的摘要文件:
上面咱們講到了一個Sealed屬性:
Name: javax/servlet/internal/ Sealed: true
這個屬性的意思是,javax/servlet/internal/包中的全部類必須從這個jar包中加載。
這個屬性主要是從jar包的安全性來考慮的。
本文已收錄於 http://www.flydean.com/java-jar-in-detail/最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!