spring-boot-maven-plugin插件打包和java -jar命令執(zhí)行原理分析
1. Maven生命周期
Maven的生命周期有三種:
- clean:清除項目構建數(shù)據(jù),較為簡單,不深入探討;
- site:建立和部署項目站點,使用的較少,也不深入探討;
- default:定義了項目構建時所需要的所有步驟,是Maven生命周期中最核心最重要的的部分。
本次要深入了解的便是default流程。其生命周期如下:
| 階段 | 可否執(zhí)行 | 說明 |
|---|---|---|
| validate | √ | 驗證項目是否正確以及所有必要信息是否可用 |
| initialize | X | 初始化構建狀態(tài) |
| generate-sources | X | 生成編譯階段需要的所有源碼文件 |
| process-sources | X | 處理源碼文件,例如過濾某些值 |
| generate-resources | X | 生成項目打包階段需要的資源文件 |
| process-resources | X | 處理資源文件,并復制到輸出目錄,為打包階段做準備 |
| compile | √ | 編譯源代碼,并移動到輸出目錄 |
| process-classes | X | 處理編譯生成的字節(jié)碼文件 |
| generate-test-sources | X | 生成編譯階段需要的測試源代碼 |
| process-test-sources | X | 處理測試資源,并復制到測試輸出目錄 |
| test-compile | X | 編譯測試源代碼并移動到測試輸出目錄中 |
| test | √ | 使用適當?shù)膯卧獪y試框架(如junit)運行測試 |
| prepare-package | X | 在真正打包前執(zhí)行一些必要的操作 |
| package | √ | 獲取編譯后的代碼,并按照可發(fā)布的格式進行打包,如jar、war或ear文件 |
| pre-integration-test | X | 在集成測試執(zhí)行之前,執(zhí)行所需的操作,例如設置環(huán)境變量 |
| integration-test | X | 處理和部署所需的包到集成測試能夠運行的環(huán)境中 |
| post-integration-test | X | 在集成測試被執(zhí)行后執(zhí)行必要的操作,例如清理環(huán)境 |
| verify | √ | 對集成測試的結果進行檢查,以保證質(zhì)量達標 |
| install | √ | 安裝打包的項目到本地倉庫,以供本地其它項目使用 |
| deploy | √ | 拷貝最終的包文件到遠程倉庫中,以共享給其它開發(fā)人員和項目 |
其中可以在Maven常見的Lifecycle中直接執(zhí)行的有validate、compile、test、package、verify和deploy七種,一般在Maven的plugin標簽中,可以通過配置如下配置來指定插件在某個階段生效,需要注意的是不可隨意配置,每個插件可處理的階段都是不同的。(不配置則執(zhí)行插件默認的)
<executions>
<execution>
<phase>XX</phase>
<goals>
<goal>XXXX</goal>
</goals>
</execution>
</executions>今天要深入了解的spring-boot-maven-plugin插件就是在package階段中生效的。
2. jar包結構
通常而言,jar包分為可執(zhí)行jar包和不可執(zhí)行jar包,顧名思義,可執(zhí)行jar包即可通過命令java -jar直接執(zhí)行,不可執(zhí)行jar包通過命令java -jar執(zhí)行則會報錯。
2.1 不可執(zhí)jar包結構
|-- _jar包根目錄
|-- 原項目class文件和resource文件
|-- _META-INF
|-- MANIFEST.MF
|-- _maven
|-- _項目目錄
|-- 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
這五項是最基本的,如果使用java -jar執(zhí)行這些jar包,將會拋出錯誤碼java.launcher.jar.error3,意為沒找到Main-Class屬性。不同語言展示的最終描述不同,由launcher+對應語言類轉(zhuǎn)換,簡體中文在launcher_zh_CN類中轉(zhuǎn)換,{0}為jar包名稱,內(nèi)容如下:
{0}中沒有主清單屬性英文在launcher類中轉(zhuǎn)換,{0}為jar包名稱,內(nèi)容如下:
no main manifest attribute, in {0}2.2 可執(zhí)行jar包結構
可執(zhí)行jar包結構挑選經(jīng)典的springboot啟動包來做示范:
|-- _jar包根目錄
|-- _BOOT-INF
|-- _classes
|-- 原項目class文件和resource文件
|-- _lib
|--原項目依賴的jar庫文件
|-- _META-INF
|-- MANIFEST.MF
|-- spring-configuration-metadata.json(springboot項目特有)
|-- build-info.properties
|-- _maven
|-- _項目目錄
|-- pom.properties
|-- pom.xml上一節(jié)我們得知了如果在MANIFEST.MF中沒有Main-Class屬性,使用java -jar命令執(zhí)行jar包會報錯,接下來看看在可執(zhí)行jar包的結構,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
里面有兩個很重要的屬性:Start-Class和Main-Class,其中Start-Class指的是項目中springboot的SpringApplication啟動類,而Main-Class則是jar包的啟動類入口。
3. spring-boot-maven-plugin插件打包

springboot打包插件執(zhí)行原理:
- 讀取原jar包:Maven插件都能讀
MavenProject對象內(nèi)容,從中可以讀取到Artifact信息,調(diào)用該對象的getFile()方法即可獲取原jar包文件對象; - 讀取項目依賴jar庫:直接使用
MavenProject對象的getArtifacts()方法即可獲取依賴的jar庫; - 加載
launchScript:讀取embeddedLaunchScript配置,并構建LaunchScript對象; - 重新改寫
MANIFEST.MF:到此步驟開始為repackage的核心流程,改寫清單文件時最主要的便是寫入Start-Class和Main-Class屬性,除此之外還會寫入jar庫和原項目文件目錄屬性; - 寫入
spring-boot-loader包文件:該包是springboot對接java -jar執(zhí)行命令的核心處理邏輯,springboot打包后加入的Main-Class: org.springframework.boot.loader.JarLauncher屬性指向的類便是此包中的jar包啟動類,如果war包則會寫入war包啟動類; - 寫入原項目文件:原項目文件會被挪到
BOOT-INF/classes/目錄下; - 寫入項目依賴jar庫:原項目依賴的jar庫會被寫入到
BOOT-INF/lib/目錄下。
如果要看spring-boot-maven-plugin插件打包源碼以分析原理,可導入插件的依賴,此時就能看到該插件的源碼。
如果使用的是IDEA,下載源碼后打上斷點,在執(zhí)行package時,使用debug模式啟動也能直接進行調(diào)試。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>XXXXX</version>
</dependency>4. 執(zhí)行jar原理
將會分析執(zhí)行java -jar命令后,Java程序調(diào)用到Springboot啟動類main方法的流程。

- JVM啟動,執(zhí)行加載主函數(shù)
LoadMainClass:此時是在JVM底層實現(xiàn)的,里面指定了LauncherHelper類; - 執(zhí)行
LauncherHelper的checkAndLoadMain方法:JVM將會調(diào)用LauncherHelper的checkAndLoadMain方法,解析并校驗jar包,并獲取主要的啟動類; - 解析jar的
MANIFEST.MF文件:在此方法中會完成讀取MANIFEST.MF文件,主要是讀取其中的Main-Class屬性,并做jar包啟動的校驗; GetStaticMethodID方法:JVM獲取到Main-Class類對象,調(diào)用Main-Class類對象的main方法;- 執(zhí)行
JarLauncher的main方法:JarLauncher繼承自Launcher,main方法最后還是會調(diào)用到Launcher.launch()方法中; - 讀取jar的
Start-Class:此時會讀取jar包的Start-Class屬性,該屬性就是原項目的SpringApplication啟動類; - 調(diào)用啟動類的main方法:調(diào)用MainMethodRunner的run方法,里面會調(diào)用Start-Class類的main方法
- 此時調(diào)入到自定義的啟動類中,完成啟動Springboot程序的入口程序。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
解析SpringBoot中使用LoadTimeWeaving技術實現(xiàn)AOP功能
這篇文章主要介紹了SpringBoot中使用LoadTimeWeaving技術實現(xiàn)AOP功能,AOP面向切面編程,通過為目標類織入切面的方式,實現(xiàn)對目標類功能的增強,本文給大家介紹的非常詳細,需要的朋友可以參考下2022-09-09
在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程
這篇文章主要介紹了在Ubuntu系統(tǒng)下安裝JDK和Tomcat的教程,這樣便是在Linux系統(tǒng)下搭建完整的Java和JSP開發(fā)環(huán)境,需要的朋友可以參考下2015-08-08

