SpringBoot工程打包后執(zhí)行Java?-Jar就能啟動(dòng)的步驟原理
本文主要分享SpringBoot工程項(xiàng)目如何打包成一個(gè)可直接通過(guò)java -jar執(zhí)行的jar,并且簡(jiǎn)單分析其啟動(dòng)步驟原理。
1.SpringBoot如何打包成一個(gè)可執(zhí)行jar?
SpringBoot打包成成一個(gè)可執(zhí)行jar需要依賴一個(gè)maven打包插件spring-boot-maven-plugin,如下所示在pom文件結(jié)尾的build節(jié)點(diǎn)添加依賴,同時(shí)將src/main/java和src/main/resources打入jar里面。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>此時(shí)再執(zhí)行maven package打包成jar(最后打包完成在target目錄下):

此時(shí)我們直接通過(guò)cmd控制臺(tái)執(zhí)行命令:java -jar boot-first-1.0.0-SNAPSHOT.jar 可以看到啟動(dòng)成功。

相關(guān)日志已經(jīng)打印出來(lái)了,此時(shí)我們?cè)偻ㄟ^(guò)postman驗(yàn)證一下編碼中的接口是否生效:

程序中的接口代碼如下:

2.SpringBoot打包成的jar為何可以直接Java -jar執(zhí)行?
java程序的入口是main方法 ,如果一個(gè)jar能夠通過(guò)java -jar執(zhí)行起來(lái),肯定離不開main方法。那springboot通過(guò)spring-boot-maven-plugin插件打包成的jar通常稱為fat jar,里面不僅僅包含了程序代碼還包括其他引用的依賴的jar。那這個(gè)fat jar的main方法入口在哪兒呢?我們通過(guò)解壓該jar可以看到,在META-INF下的有一個(gè)MANIFEST.MF文件:

其中MANIFEST.MF中內(nèi)容如下,其中比較重要的幾行信息:
Start-Class: com.xren.bootfirst.BootFirstApplication ##程序開始類
Spring-Boot-Classes: BOOT-INF/classes/ ##加載的class
Spring-Boot-Lib: BOOT-INF/lib/ ##加載的內(nèi)部jar位置
Main-Class: org.springframework.boot.loader.JarLauncher ## boot啟動(dòng)類

綜上可知,SpringBoot項(xiàng)目通過(guò)引入打包插件spring-boot-maven-plugin生成特定的主清單文件MANIFEST.MF,其中包含了程序的入口類和相關(guān)啟動(dòng)依賴。
3.一窺SpringBoot初啟動(dòng)
通用的jar包如果能被java -jar執(zhí)行,只需要其MANIFEST.MF文件中有 Main-Class配置項(xiàng)即可。而此處SpringBoot的jar中的MANIFEST.MF文件里面不僅僅包含Main-Class 還有Start-Class、Spring-Boot-Classes、Spring-Boot-Lib等相關(guān)信息,那他們是如何運(yùn)行的呢?我們還是要回到該jar主類org.springframework.boot.loader.JarLauncher來(lái)一窺究竟。
查看org.springframework.boot.loader.JarLauncher類需要pom引入spring-boot-loader,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>引入后其中JarLaunche類代碼如下,它包含一個(gè)main方法,并且繼承一個(gè)父類ExecutableArchiveLauncher。
public class JarLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
protected boolean isNestedArchive(Entry entry) {
return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
}
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
}從main方法進(jìn)入我們需要關(guān)注一個(gè)launch方法,其中的三行
- 注冊(cè)一個(gè)jar處理類
- 獲取一個(gè)類加載器(通過(guò)路徑加載lib和classs)
- 通過(guò)獲取主啟動(dòng)類和類加載器運(yùn)行啟動(dòng)。
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
this.launch(args, this.getMainClass(), classLoader);
}追尋this.getMainClass() 方法,其會(huì)指向上述的父類ExecutableArchiveLauncher中,此處就回到了我們開始說(shuō)的Start-Class配置項(xiàng)。
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
} else {
return mainClass;
}
}getMainClass()方法中的this.archive.getManifest() 就是獲取jar下的META-INF/MANIFEST.MF文件。此處追尋到Archive的子類ExplodedArchive中,如下代碼獲取清單文件:
private File getManifestFile(File root) {
File metaInf = new File(root, "META-INF");
return new File(metaInf, "MANIFEST.MF");
}到此時(shí)清單文件中的Start-Class、Spring-Boot-Classes、Spring-Boot-Lib都已找到,準(zhǔn)備啟動(dòng)!
回到org.springframework.boot.loader.Launcher的launch方法。
- 當(dāng)前線程設(shè)置類加載器
- 創(chuàng)建主方法并運(yùn)行(主方法即為Start-Class指向的類)
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(mainClass, args, classLoader).run();
}以上兩行代碼具體實(shí)現(xiàn)在org.springframework.boot.loader.MainMethodRunner類中,run方法即為最后的啟動(dòng)邏輯
- 通過(guò)當(dāng)前線程類加載器載入清單文件Start-Class配置的主類。
- 加載后獲取主類中的main方法(XXXApplication中的main方法)。
- 反射執(zhí)行該XXXApplication中的main方法。
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = args != null ? (String[])args.clone() : null;
}
public void run() throws Exception {
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke((Object)null, this.args);
}至此,從執(zhí)行java -jar命令,SpringBoot打包的jar啟動(dòng)到程序中XXXApplication中的main方法這一階段我們就初步分析完了!
總結(jié):SpringBoot通過(guò)引入打包插件spring-boot-maven-plugin將其相關(guān)信息寫入到j(luò)ava-jar的META-INF/MANIFEST.MF文件中,而后通過(guò)清單文件中的主入口Main-Class配置的org.springframework.boot.loader.JarLauncher類加載相關(guān)class、lib和應(yīng)用程序主類Start-Class配置的XXXApplication類,再反射獲取XXXApplication類中我們業(yè)務(wù)定義的main方法啟動(dòng)運(yùn)行。
到此這篇關(guān)于SpringBoot工程打包后為何執(zhí)行Java -Jar就能啟動(dòng)?的文章就介紹到這了,更多相關(guān)springboot打包執(zhí)行ava -Jar啟動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jpa?onetomany?使用級(jí)連表刪除被維護(hù)表數(shù)據(jù)時(shí)的坑
這篇文章主要介紹了jpa?onetomany?使用級(jí)連表刪除被維護(hù)表數(shù)據(jù)時(shí)的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
關(guān)于java String中intern的深入講解
這篇文章主要給大家介紹了關(guān)于java String中intern的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
idea配置springboot熱部署終極解決辦法(解決熱部署失效問(wèn)題)
這篇文章主要介紹了idea配置springboot熱部署終極解決辦法(解決熱部署失效問(wèn)題),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-07-07
java實(shí)現(xiàn)簡(jiǎn)單的驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的驗(yàn)證碼功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
java數(shù)據(jù)結(jié)構(gòu)與算法之桶排序?qū)崿F(xiàn)方法詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之桶排序?qū)崿F(xiàn)方法,結(jié)合具體實(shí)例形式詳細(xì)分析了桶排序的概念、原理、實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下2017-05-05
Java?入門圖形用戶界面設(shè)計(jì)之事件處理下
圖形界面(簡(jiǎn)稱GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶來(lái)說(shuō)在視覺(jué)上更易于接受,本篇精講Java語(yǔ)言中關(guān)于圖形用戶界面的事件處理2022-02-02
在Spring項(xiàng)目中引入高版本依賴并解決低版本沖突問(wèn)題的解決方法
在Spring項(xiàng)目的開發(fā)過(guò)程中,依賴管理是一個(gè)非常重要且復(fù)雜的問(wèn)題,我們可能需要引入更高版本的依賴來(lái)使用新特性或修復(fù)舊版本的Bug,然而,這些高版本依賴可能會(huì)與項(xiàng)目中已有的低版本依賴產(chǎn)生沖突,本文將詳細(xì)探討如何在Spring中引入高版本依賴,并解決低版本依賴沖突的問(wèn)題2025-03-03

