自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么SpringBoot可以直接運(yùn)行 Jar 包?

開發(fā) 架構(gòu)
JAR 文件格式以流行的 ZIP 文件格式為基礎(chǔ)。與 ZIP 文件不同的是,JAR 文件不僅用于壓縮和發(fā)布,而且還用于部署和封裝庫、組件和插件程序,并可被像編譯器和 JVM 這樣的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用來指示工具如何處理特定的 JAR。

小伙伴們好呀,今天咋們來探索下,為什么 SpringBoot 的 jar 包可以直接運(yùn)行? 以及 4ye 踩到的坑????

目錄如下 ??

開始之前,先簡單介紹下這個 jar ??

什么是 jar

JAR 文件格式以流行的 ZIP 文件格式為基礎(chǔ)。與 ZIP 文件不同的是,JAR 文件不僅用于壓縮和發(fā)布,而且還用于部署和封裝庫、組件和插件程序,并可被像編譯器和 JVM 這樣的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用來指示工具如何處理特定的 JAR?!栋俣劝倏啤?/p>

jar包結(jié)構(gòu)圖

這里小伙伴們可以自行查找下 jar文件規(guī)范 ??

例如 https://blog.51cto.com/robinc/547658

規(guī)范中最重要的一點(diǎn),就是 MATE-INF 文件夾中的 MANIFEST.MF 清單文件了。

文件內(nèi)容如下

一眼看過去,這個 Main-Class 配置就特別突出了。?? 它指明了這個啟動類的位置。

當(dāng)我們用 java -jar xx.jar 命令運(yùn)行一個 jar 包時,無外乎,它肯定是幫我們找到這個 main 方法,然后啟動它。

(ps:使用 -jar 時,會忽略 classpath 環(huán)境的配置。)

這里我將上期 Map 專題的代碼進(jìn)行打包,運(yùn)行效果如下

可以看到第一次運(yùn)行時出現(xiàn)沒有主清單屬性的提示 。

在 pom 文件中添加 Main-class 配置(如下??),即可解決問題。

<build>
<finalName>Map</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>cn.java4ye.HashMapMain</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

那么,到這里,基本的秘密已經(jīng)被我們知道了。

執(zhí)行 java -jar xx.jar 命令時,會去:

解析 MATE-INF 文件夾中的MANIFEST.MF 清單文件,

然后找到 Main-class ,運(yùn)行其中的 main方法。

接著我們再反過來看看這個 Springboot 的 jar 包有啥不同。

官方文檔參考

建議大家先去讀讀這個文檔~??

地址:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar

沒看文檔前,我想得很簡單,直接就打開 Springboot 打包好的 jar 包去找 META-INF 文件夾下的 MANIFEST.MF

看到后,我的理解如下圖

其他配置應(yīng)該是表明這個 classes ,lib 去哪里找。

實(shí)際上呢,這個理解也沒有錯,但是里面多了很多細(xì)節(jié)~

比如:

  • 嵌套的 jar 包要怎么解決
  • classpath.idx 和 layers.idx 是用來干嘛的
  • jarmode 又是什么

下面就讓我們來了解探索下它的奧秘叭。(也順便看看 4ye 掉到怎樣的牛角尖去了叭??)

META-INF

我們可以看到,Springboot 插件打包后生成的 jar 包和原來是有很大的不同的。

除了 META-INF 文件夾結(jié)構(gòu)沒變化之外。

BOOT-INF

結(jié)構(gòu)如下??

這里的重點(diǎn)在 classpath.idx 和 layers.idx 這兩個索引文件。

classpath.idx 可以被 jar 和 war 包使用,它配置了哪些 jar 包要被加載到 classpath 中。

layers.idx 只能被 jar 包使用,在 創(chuàng)建鏡像 的時候被使用,如 Docker/OCI (OCI是一種容器標(biāo)準(zhǔn))

原話如下:

The layers.idx file can be used only for jars, and it allows a jar to be split into logical layers for Docker/OCI image creation.

官方文檔中詳細(xì)說明了這個 layers.idx 的規(guī)范。

比如 "dependencies" 是這部分 layer 的名稱,下面的都屬于這個 layer。

org

這里就是 Springboot 運(yùn)行 jar 包的秘密了。

org文件夾

這里先介紹下一些背景,然后再簡單看看源碼~??

背景一

Fat Jar 指的就是這種 jar in jar , 或者說嵌套的 Jar 包。但是 Java 中并沒有能加載嵌套 Jar 的方式,所以 Spring boot 自己寫了這套代碼,來解決這個問題。

當(dāng)然,這句話是從 Springboot 官方文檔中發(fā)現(xiàn)的

Java does not provide any standard way to load nested jar files (that is, jar files that are themselves contained within a jar).

到了這里,我就掉入一個坑了。。?? 因?yàn)閷@塊不熟悉,我一直以為它說的就是 URLClassLoader 無法加載到這里面的class,可是我測試了好多遍,發(fā)現(xiàn)嵌套在里面的 class 可以被加載到呀??(自己挖的坑??,文末解答)

我翻了很多資料,還去查看 GitHub 的 issue,發(fā)現(xiàn)都沒有人提過相關(guān)的問題??

到了這里,我已經(jīng)非常非常無奈了!這痛苦的感覺,就像剛開始學(xué)習(xí)編程時,裝環(huán)境被各種奇奇怪怪的 bug 搞到漸漸沒脾氣。

被折磨了 N 久之后,我又找了另外一個角度,難道它說的是這個 JarFile 。我的天,我嘗試了一下后,發(fā)現(xiàn)確實(shí)沒有辦法加載嵌套的 Jar

證明如下 ??

背景二

URLStreamHandler ,它是用來處理各種協(xié)議的(比如 http,file,jar 等等),配合 URL,URLConnection 可以加載相應(yīng)的資源。

比如 上面的例子中用到這個協(xié)議 jar:file:/xx.jar ,那么加載資源時,它就會使用這個 jdk jar 包下的 handler 來進(jìn)行處理

關(guān)于協(xié)議的擴(kuò)展可以看這里

https://blog.csdn.net/xiaomin1991222/article/details/50980754

而Springboot 也是重寫了這個 handler 來處理嵌套的 jar 資源。

源碼

核心代碼便是這個 JarFile 以及下面要說到的Launcher 了。??

根據(jù) MANIFEST.MF 中指定的 main-class,我們可以發(fā)現(xiàn)如下代碼

launch 方法如下

第一步,注冊協(xié)議,registerUrlProtocolHandler

這里就涉及到這個 URLStreamHandler 機(jī)制協(xié)議了

我們可以通過 JVM 啟動參數(shù) -D java.protocol.handler.pkgs 來設(shè)置 URLStreamHandler 實(shí)現(xiàn)類的包路徑

這里的代碼也是通過這個系統(tǒng)參數(shù)將 URLStreamHandler 實(shí)現(xiàn)類的包路徑 設(shè)置為 loader 包下的。

第二步是 創(chuàng)建 classLoader 。

這里可以看到這些 url 的格式如下

第三步是 判斷是否有 jarmode 參數(shù) 。這個是和 docker 鏡像相關(guān)的。

官方文檔 ??

https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-container-images

用來簡化提取 layer 的操作。

比如以前要寫很多 copy,而使用 jarmode 就會自動去 layers.idx 中提取了。(下期再寫~)

由于我們沒有添加該參數(shù),所以這里是執(zhí)行 getMainClass() 方法,來獲取到這個啟動類的。

第四步,運(yùn)行 run 方法。

這里就創(chuàng)建 MainMethodRunner 類,并執(zhí)行其中的 run 方法,去反射運(yùn)行這個 main 方法了??

小結(jié)一下

除了 JarLauncher 外,源碼中的 WarLauncher 和 PropertiesLauncher 也擁有 main 方法,小伙伴們可以自己看看~

JarLauncher和 WarLauncher 都是繼承這個ExecutableArchiveLauncher 的。

大坑??

到了這里,我陷入了沉思 ??

想到了之前寫這個 AOP 插件( 《AOP 插件就這?上手不用兩分鐘!!》)時,好像也遇到了一點(diǎn)小困難和 jar 包相關(guān)的。再次翻看后,我發(fā)現(xiàn)我那會寫了這么一個注釋在 pom 文件中,去掉后打包確實(shí)報了這么一個錯誤。

"用 Springboot 插件打包,但是我們沒有 main 方法,會報錯,這里跳過就好了"

隨后我又在之前的文章中看到了這么一段記錄,這里也提到了 jar 包結(jié)構(gòu)的變化 BOOT-INF/classes/ ,還有引出一個問題 ——“URLClassLoader 無法正確加載類,一直出現(xiàn) ClassNotFoundException ”

到這里,我已經(jīng)無限好奇了。然后就不自覺地掉進(jìn)這個牛角尖里了……

為啥 URLClassLoader 就無法加載到這些類了呢? ??

我嘗試用 URLClassLoader 去讀取 Springboot 插件打包出來的 jar 包,發(fā)現(xiàn) BOOT-INF/classes/ 下面的類一直無法讀取到。但是這個 org 文件夾中的就可以正常加載。

同時我也發(fā)現(xiàn)一點(diǎn)不對勁! 沒錯,在最開始的證明這里,明明可以加載到 BOOT-INF/classes/ 下面的類呀!

于是我做了 n 遍驗(yàn)證,發(fā)現(xiàn)在插件篇中,這個 BOOT-INF/classes/ 下面的類一直無法讀取到,會一直報錯。這我就很納悶了,直到我發(fā)現(xiàn)現(xiàn)在做 demo 的這個項(xiàng)目里,在 pom 文件中就引入了這個 jar 包!我的天~ 去掉之后它也一直報錯了,坑死自己了??

所以之前在 《AOP 插件就這?上手不用兩分鐘!!》 一文中提到的結(jié)論是沒錯的。而這也更加驗(yàn)證了 Java 中并沒有能加載嵌套 Jar 的方式 ,所以 Springboot 才重寫了它的。

而重寫后,資源的路徑文件夾路徑變成下面這種方式了!!

上面這個圖是 直到,我發(fā)現(xiàn)在 IDEA 中可以直接 debug jar 包 ?? 才得來的…… 不然現(xiàn)在還在那里卡著呢??

總結(jié)

本期思維導(dǎo)圖可以在這里獲取

https://www.processon.com/embed/62089eed0e3e7407d1cd9ee7

那么 Springboot jar 包為啥可以運(yùn)行呢?

答:

執(zhí)行 java -jar xx.jar 命令時,會去 解析MATE-INF 文件夾中的 MANIFEST.MF 清單文件,然后找到 Main-class ,反射運(yùn)行其中的 main 方法。這個是最根本的原因。

而 Springboot maven 插件打包后的 jar 包結(jié)構(gòu)有所變動,新增 org loader 代碼目錄和 BOOT-INF 目錄,META-INF 目錄不變,但是其中的 MANIFEST.MF 發(fā)生改變,其中新增 Start-Class 表示真正的啟動類,而原本的 Main-Class 則指向JarLauncher , JarLauncher 啟動時會去 注冊協(xié)議,創(chuàng)建 ClassLoader,加載并反射運(yùn)行 Start-Class 中的 main 方法,來啟動程序。重寫 Jar 協(xié)議是在 Spring boot loader源碼中的 JarFile 中進(jìn)行的,同時重新實(shí)現(xiàn) URLStreamHandler 來解決 嵌套Jar 的問題。


責(zé)任編輯:武曉燕 來源: Java4ye
相關(guān)推薦

2024-04-03 09:01:34

SpringTomcat容器

2023-11-30 08:16:19

SpringjarTomcat

2020-05-07 16:30:32

Spring BootJava

2024-11-26 08:36:56

SpringJar機(jī)制

2015-08-17 10:16:00

CentOSDocker命令root

2020-03-19 08:59:15

SpringMVC啟動過程

2020-08-24 15:56:49

AndroidWindows 10三星

2023-04-04 22:23:09

2009-06-29 18:35:41

操作系統(tǒng)服務(wù)器軟件

2019-03-06 13:45:20

Windows 10Xbox微軟

2023-09-01 08:26:06

SpringBootjar包war包

2020-08-27 11:35:36

Python 開發(fā)編程語言

2021-04-16 17:02:21

數(shù)組C++語言

2020-11-11 16:46:35

蘋果macOS操作系統(tǒng)

2024-09-14 07:00:28

SpringBoot代碼反編譯

2024-09-13 08:57:25

SpringJar項(xiàng)目

2020-10-22 14:20:39

Parallels

2024-05-29 10:43:31

2015-05-12 10:34:45

2024-04-07 00:00:00

服務(wù)器瀏覽器AI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號