spring-boot-maven-plugin插件打包和java -jar命令執(zhí)行原理分析
1. Maven生命周期
Maven的生命周期有三種:
- clean:清除項(xiàng)目構(gòu)建數(shù)據(jù),較為簡(jiǎn)單,不深入探討;
- site:建立和部署項(xiàng)目站點(diǎn),使用的較少,也不深入探討;
- default:定義了項(xiàng)目構(gòu)建時(shí)所需要的所有步驟,是Maven生命周期中最核心最重要的的部分。
本次要深入了解的便是default流程。其生命周期如下:
階段 | 可否執(zhí)行 | 說(shuō)明 |
---|---|---|
validate | √ | 驗(yàn)證項(xiàng)目是否正確以及所有必要信息是否可用 |
initialize | X | 初始化構(gòu)建狀態(tài) |
generate-sources | X | 生成編譯階段需要的所有源碼文件 |
process-sources | X | 處理源碼文件,例如過(guò)濾某些值 |
generate-resources | X | 生成項(xiàng)目打包階段需要的資源文件 |
process-resources | X | 處理資源文件,并復(fù)制到輸出目錄,為打包階段做準(zhǔn)備 |
compile | √ | 編譯源代碼,并移動(dòng)到輸出目錄 |
process-classes | X | 處理編譯生成的字節(jié)碼文件 |
generate-test-sources | X | 生成編譯階段需要的測(cè)試源代碼 |
process-test-sources | X | 處理測(cè)試資源,并復(fù)制到測(cè)試輸出目錄 |
test-compile | X | 編譯測(cè)試源代碼并移動(dòng)到測(cè)試輸出目錄中 |
test | √ | 使用適當(dāng)?shù)膯卧獪y(cè)試框架(如junit)運(yùn)行測(cè)試 |
prepare-package | X | 在真正打包前執(zhí)行一些必要的操作 |
package | √ | 獲取編譯后的代碼,并按照可發(fā)布的格式進(jìn)行打包,如jar、war或ear文件 |
pre-integration-test | X | 在集成測(cè)試執(zhí)行之前,執(zhí)行所需的操作,例如設(shè)置環(huán)境變量 |
integration-test | X | 處理和部署所需的包到集成測(cè)試能夠運(yùn)行的環(huán)境中 |
post-integration-test | X | 在集成測(cè)試被執(zhí)行后執(zhí)行必要的操作,例如清理環(huán)境 |
verify | √ | 對(duì)集成測(cè)試的結(jié)果進(jìn)行檢查,以保證質(zhì)量達(dá)標(biāo) |
install | √ | 安裝打包的項(xiàng)目到本地倉(cāng)庫(kù),以供本地其它項(xiàng)目使用 |
deploy | √ | 拷貝最終的包文件到遠(yuǎn)程倉(cāng)庫(kù)中,以共享給其它開(kāi)發(fā)人員和項(xiàng)目 |
其中可以在Maven常見(jiàn)的Lifecycle中直接執(zhí)行的有validate、compile、test、package、verify和deploy
七種,一般在Maven的plugin標(biāo)簽中,可以通過(guò)配置如下配置來(lái)指定插件在某個(gè)階段生效,需要注意的是不可隨意配置,每個(gè)插件可處理的階段都是不同的。(不配置則執(zhí)行插件默認(rèn)的)
<executions> <execution> <phase>XX</phase> <goals> <goal>XXXX</goal> </goals> </execution> </executions>
今天要深入了解的spring-boot-maven-plugin
插件就是在package
階段中生效的。
2. jar包結(jié)構(gòu)
通常而言,jar包分為可執(zhí)行jar包和不可執(zhí)行jar包,顧名思義,可執(zhí)行jar包即可通過(guò)命令java -jar
直接執(zhí)行,不可執(zhí)行jar包通過(guò)命令java -jar
執(zhí)行則會(huì)報(bào)錯(cuò)。
2.1 不可執(zhí)jar包結(jié)構(gòu)
|-- _jar包根目錄 |-- 原項(xiàng)目class文件和resource文件 |-- _META-INF |-- MANIFEST.MF |-- _maven |-- _項(xiàng)目目錄 |-- pom.properties |-- pom.xml
上面是經(jīng)典的不可執(zhí)行jar包目錄,其中MANIFEST.MF
文件內(nèi)容如下:
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Built-By: xxxxx Created-By: Apache Maven 3.5.0 Build-Jdk: 1.8.0_151
這五項(xiàng)是最基本的,如果使用java -jar
執(zhí)行這些jar包,將會(huì)拋出錯(cuò)誤碼java.launcher.jar.error3
,意為沒(méi)找到Main-Class
屬性。不同語(yǔ)言展示的最終描述不同,由launcher
+對(duì)應(yīng)語(yǔ)言類(lèi)轉(zhuǎn)換,簡(jiǎn)體中文在launcher_zh_CN
類(lèi)中轉(zhuǎn)換,{0}為jar包名稱(chēng),內(nèi)容如下:
{0}中沒(méi)有主清單屬性
英文在launcher
類(lèi)中轉(zhuǎn)換,{0}為jar包名稱(chēng),內(nèi)容如下:
no main manifest attribute, in {0}
2.2 可執(zhí)行jar包結(jié)構(gòu)
可執(zhí)行jar包結(jié)構(gòu)挑選經(jīng)典的springboot啟動(dòng)包來(lái)做示范:
|-- _jar包根目錄 |-- _BOOT-INF |-- _classes |-- 原項(xiàng)目class文件和resource文件 |-- _lib |--原項(xiàng)目依賴(lài)的jar庫(kù)文件 |-- _META-INF |-- MANIFEST.MF |-- spring-configuration-metadata.json(springboot項(xiàng)目特有) |-- build-info.properties |-- _maven |-- _項(xiàng)目目錄 |-- pom.properties |-- pom.xml
上一節(jié)我們得知了如果在MANIFEST.MF
中沒(méi)有Main-Class
屬性,使用java -jar
命令執(zhí)行jar包會(huì)報(bào)錯(cuò),接下來(lái)看看在可執(zhí)行jar包的結(jié)構(gòu),MANIFEST.MF
中具體有什么屬性:
Manifest-Version: 1.0 Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx Implementation-Title: XXXX Implementation-Version: 1.0-SNAPSHOT Spring-Boot-Layers-Index: BOOT-INF/layers.idx Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.1.6.RELEASE Created-By: Maven Archiver 3.4.0 Start-Class: XXX.XXX.XXX.XXXX Main-Class: org.springframework.boot.loader.JarLauncher
里面有兩個(gè)很重要的屬性:Start-Class
和Main-Class
,其中Start-Class
指的是項(xiàng)目中springboot的SpringApplication啟動(dòng)類(lèi),而Main-Class
則是jar包的啟動(dòng)類(lèi)入口。
3. spring-boot-maven-plugin插件打包
springboot打包插件執(zhí)行原理:
- 讀取原jar包:Maven插件都能讀
MavenProject
對(duì)象內(nèi)容,從中可以讀取到Artifact
信息,調(diào)用該對(duì)象的getFile()
方法即可獲取原jar包文件對(duì)象; - 讀取項(xiàng)目依賴(lài)jar庫(kù):直接使用
MavenProject
對(duì)象的getArtifacts()
方法即可獲取依賴(lài)的jar庫(kù); - 加載
launchScript
:讀取embeddedLaunchScript
配置,并構(gòu)建LaunchScript
對(duì)象; - 重新改寫(xiě)
MANIFEST.MF
:到此步驟開(kāi)始為repackage
的核心流程,改寫(xiě)清單文件時(shí)最主要的便是寫(xiě)入Start-Class
和Main-Class
屬性,除此之外還會(huì)寫(xiě)入jar庫(kù)和原項(xiàng)目文件目錄屬性; - 寫(xiě)入
spring-boot-loader
包文件:該包是springboot對(duì)接java -jar
執(zhí)行命令的核心處理邏輯,springboot打包后加入的Main-Class: org.springframework.boot.loader.JarLauncher
屬性指向的類(lèi)便是此包中的jar包啟動(dòng)類(lèi),如果war包則會(huì)寫(xiě)入war包啟動(dòng)類(lèi); - 寫(xiě)入原項(xiàng)目文件:原項(xiàng)目文件會(huì)被挪到
BOOT-INF/classes/
目錄下; - 寫(xiě)入項(xiàng)目依賴(lài)jar庫(kù):原項(xiàng)目依賴(lài)的jar庫(kù)會(huì)被寫(xiě)入到
BOOT-INF/lib/
目錄下。
如果要看spring-boot-maven-plugin
插件打包源碼以分析原理,可導(dǎo)入插件的依賴(lài),此時(shí)就能看到該插件的源碼。
如果使用的是IDEA,下載源碼后打上斷點(diǎn),在執(zhí)行package
時(shí),使用debug模式啟動(dòng)也能直接進(jìn)行調(diào)試。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>XXXXX</version> </dependency>
4. 執(zhí)行jar原理
將會(huì)分析執(zhí)行java -jar
命令后,Java程序調(diào)用到Springboot啟動(dòng)類(lèi)main方法的流程。
- JVM啟動(dòng),執(zhí)行加載主函數(shù)
LoadMainClass
:此時(shí)是在JVM底層實(shí)現(xiàn)的,里面指定了LauncherHelper
類(lèi); - 執(zhí)行
LauncherHelper
的checkAndLoadMain
方法:JVM將會(huì)調(diào)用LauncherHelper
的checkAndLoadMain
方法,解析并校驗(yàn)jar包,并獲取主要的啟動(dòng)類(lèi); - 解析jar的
MANIFEST.MF
文件:在此方法中會(huì)完成讀取MANIFEST.MF
文件,主要是讀取其中的Main-Class
屬性,并做jar包啟動(dòng)的校驗(yàn); GetStaticMethodID
方法:JVM獲取到Main-Class
類(lèi)對(duì)象,調(diào)用Main-Class
類(lèi)對(duì)象的main方法;- 執(zhí)行
JarLauncher
的main方法:JarLauncher
繼承自Launcher
,main方法最后還是會(huì)調(diào)用到Launcher.launch()
方法中; - 讀取jar的
Start-Class
:此時(shí)會(huì)讀取jar包的Start-Class
屬性,該屬性就是原項(xiàng)目的SpringApplication
啟動(dòng)類(lèi); - 調(diào)用啟動(dòng)類(lèi)的main方法:調(diào)用MainMethodRunner的run方法,里面會(huì)調(diào)用Start-Class類(lèi)的main方法
- 此時(shí)調(diào)入到自定義的啟動(dòng)類(lèi)中,完成啟動(dòng)Springboot程序的入口程序。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- spring-boot-maven-plugin插件爆紅問(wèn)題及解決方案
- 利用spring-boot-maven-plugin插件打包SpringBoot應(yīng)用方式
- SpringBoot Maven打包插件spring-boot-maven-plugin無(wú)法解析原因
- Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found的解決方案(親測(cè)可用)
- SpringBoot中的maven插件spring-boot-maven-plugin使用
- 解決spring-boot-maven-plugin報(bào)紅的問(wèn)題
相關(guān)文章
深入了解Java數(shù)據(jù)結(jié)構(gòu)和算法之堆
這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之堆 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01Java實(shí)現(xiàn)控制臺(tái)輸出兩點(diǎn)間距離
這篇文章主要介紹了Java實(shí)現(xiàn)控制臺(tái)輸出兩點(diǎn)間距離,涉及了部分編程坐標(biāo)的問(wèn)題,具有一定參考價(jià)值,需要的朋友可以了解下2017-09-09JAVA動(dòng)態(tài)維度笛卡爾積輸出的實(shí)現(xiàn)
本文主要介紹了JAVA動(dòng)態(tài)維度笛卡爾積輸出的實(shí)現(xiàn),通過(guò)動(dòng)態(tài)生成笛卡爾積,可以方便地處理多維數(shù)據(jù)集,提高數(shù)據(jù)處理效率,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02解析SpringBoot中使用LoadTimeWeaving技術(shù)實(shí)現(xiàn)AOP功能
這篇文章主要介紹了SpringBoot中使用LoadTimeWeaving技術(shù)實(shí)現(xiàn)AOP功能,AOP面向切面編程,通過(guò)為目標(biāo)類(lèi)織入切面的方式,實(shí)現(xiàn)對(duì)目標(biāo)類(lèi)功能的增強(qiáng),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09SpringBoot項(xiàng)目POM文件的使用小結(jié)
本文主要詳細(xì)介紹了Maven中SpringBoot項(xiàng)目的POM文件配置,包括項(xiàng)目的依賴(lài)和插件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程
這篇文章主要介紹了在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程,這樣便是在Linux系統(tǒng)下搭建完整的Java和JSP開(kāi)發(fā)環(huán)境,需要的朋友可以參考下2015-08-08