引言 在傳統(tǒng)的Java應(yīng)用程序開發(fā)和部署場景中,開發(fā)者往往需要經(jīng)歷一系列復(fù)雜的步驟才能將應(yīng)用成功部署到生產(chǎn)環(huán)境。例如,對于基于Servlet規(guī)范的Java Web應(yīng)用,開發(fā)完成后通常會被打包成WAR格式,然后部署到像Apache Tomcat、Jetty這樣的Web容器中。這一過程中,不僅要管理應(yīng)用本身的編譯產(chǎn)物,還需要處理各種第三方依賴庫的版本和加載順序,同時在服務(wù)器端進行相應(yīng)的配置以確保應(yīng)用正常運行。
隨著Spring Boot產(chǎn)生,它以其開箱即用、約定優(yōu)于配置的理念徹底改變了Java應(yīng)用的開發(fā)體驗。其中一個標(biāo)志性特征便是Spring Boot應(yīng)用可以被打包成一個可直接運行的jar文件,無需外部容器的支持。
當(dāng)提及“Spring Boot的jar可以直接運行”,我們不禁好奇:這背后究竟是怎樣的機制讓一個簡單的命令行操作就能啟動一個完整的Web服務(wù)或任何類型的Java應(yīng)用呢?本文將深入剖析Spring Boot的打包過程和運行原理,揭示其jar包是如何巧妙地集成了依賴、嵌入了Web容器、實現(xiàn)了自動配置等功能,從而使得開發(fā)人員能夠迅速地將應(yīng)用部署到任何支持Java的環(huán)境中。
springboot的jar包為什么可以直接運行.png
SpringBoot JAR包基礎(chǔ)概念 Fat JAR(也稱作Uber JAR,也被戲稱為胖Jar)是一種特殊的Java歸檔(JAR)文件,它將應(yīng)用程序所需的全部依賴庫與應(yīng)用程序自身的類文件合并到了同一個JAR文件中。在Spring Boot上下文中,F(xiàn)at JAR被用于構(gòu)建一種完全自包含且可獨立運行的應(yīng)用程序包。這樣的jar文件不僅僅包含項目的主代碼,還包括了所有必要的第三方庫、資源文件等一切運行時所需要的組件。
Fat JAR的核心特點是“自包含”,意味著只需分發(fā)這一個文件即可部署應(yīng)用,無需再額外處理眾多的依賴庫。這種形式極大地方便了應(yīng)用的快速部署與遷移,尤其適合于云端部署或者無網(wǎng)絡(luò)環(huán)境下的安裝。
而對于普通jar包來說,它通常僅包含一個模塊或應(yīng)用程序的一部分,主要用來封裝和組織Java類及相關(guān)資源。在Java生態(tài)系統(tǒng)中,一個普通的jar包可能僅是一個庫,或者一組相關(guān)功能的集合,但它不會包含其他依賴的jar包,因此在運行時需要與之相關(guān)的其他庫一起存在于類路徑中。
相比之下,F(xiàn)at JAR則解決了依賴管理的問題,通過將所有的依賴都納入其中,避免了由于類路徑設(shè)置不正確導(dǎo)致的“缺失類”或“找不到類”的問題。在Spring Boot項目中,通過Maven或Gradle插件可以輕易地構(gòu)建出這樣的Fat JAR,使得最終生成的jar文件成為一個真正的“一站式”解決方案,只需使用java -jar
命令就可以啟動整個應(yīng)用程序,無需預(yù)先配置復(fù)雜的類路徑環(huán)境。
Spring Boot應(yīng)用打包機制 Spring Boot應(yīng)用打包機制充分利用了Maven或Gradle構(gòu)建工具的強大功能,旨在簡化傳統(tǒng)Java應(yīng)用的構(gòu)建與部署流程。其核心在于創(chuàng)建一個可執(zhí)行的Fat JAR,使得開發(fā)者能夠輕松地將整個Spring Boot應(yīng)用及其依賴項打包成單個文件,從而實現(xiàn)一鍵啟動和便捷部署。
我們以Maven打包為例:
對于使用Maven構(gòu)建的Spring Boot應(yīng)用,spring-boot-maven-plugin
是關(guān)鍵插件,負責(zé)處理Fat JAR的構(gòu)建。在pom.xml文件中,通常會看到如下配置:
<build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > <configuration > <!-- 可選配置項,如mainClass屬性指定入口類 --> <mainClass > ${start-class}</mainClass > </configuration > <executions > <execution > <goals > <goal > repackage</goal > </goals > </execution > </executions > </plugin > </plugins > </build >
通過mvn package
命令,Maven首先會按照標(biāo)準(zhǔn)流程構(gòu)建項目,隨后spring-boot-maven-plugin
會執(zhí)行repackage
目標(biāo),該目標(biāo)會重新包裝已生成的標(biāo)準(zhǔn)JAR文件,將其轉(zhuǎn)換為包含所有依賴項和適當(dāng)?shù)膯悠餍畔⒌腇at JAR。這樣生成的JAR可以直接通過java -jar
命令啟動。
Spring Boot應(yīng)用打包機制均確保了生成的包不僅包含了項目本身的類,還包含了運行時所必需的所有依賴庫,以及一些特定的元數(shù)據(jù)(如MANIFEST.MF中的啟動類信息)。這一特性大大簡化了部署過程,并有助于提升應(yīng)用的可移植性和維護性。Fat jar中的內(nèi)容:
image.png
META-INF/
: 包含MANIFEST.MF文件和其他元數(shù)據(jù)信息,其中Main-Class屬性指向Spring Boot的啟動類加載器。
BOOT-INF/classes/
: 存放項目自身的類文件和資源文件。
BOOT-INF/lib/
: 放置所有依賴的jar包,包括Spring Boot starter依賴以及其他第三方庫。(如果項目中有靜態(tài)資源文件,也會在BOOT-INF下有對應(yīng)的static、templates等目錄)
Spring Boot啟動器與Loader機制 Spring Boot應(yīng)用的jar包可以直接運行主要依賴于它的啟動器以及Loader機制,而對于Loader機制主要利用MANIFEST.MF文件以及其內(nèi)部類加載邏輯。
MANIFEST.MF文件是什么? MANIFEST.MF
是JAR
文件內(nèi)的一個標(biāo)準(zhǔn)元數(shù)據(jù)文件,它包含了關(guān)于JAR包的基本信息和運行指令。在Spring Boot應(yīng)用的jar包中,MANIFEST.MF
尤為重要,因為它設(shè)置了Main-Class
屬性,指示了用于啟動整個應(yīng)用程序的類,這個類通常是org.springframework.boot.loader.JarLauncher
或其他由Spring Boot提供的啟動器類。
image.png
Main-Class
屬性指向的JarLauncher
類是Spring Boot自定義的類加載器體系的一部分。JarLauncher
繼承自org.springframework.boot.loader.Launcher
,專門用于啟動以Fat JAR
形式發(fā)布的Spring Boot應(yīng)用。JarLauncher
負責(zé)創(chuàng)建一個類加載器LaunchedURLClassLoader
。
image.png
image.png
當(dāng)通過java -jar
命令執(zhí)行Spring Boot jar包時,JVM會依據(jù)MANIFEST.MF
中的Main-Class
啟動指定的啟動器。
JarLauncher獲取MainClass源碼.png
Spring Boot的啟動器類加載器LaunchedURLClassLoader
首先會讀取MANIFEST.MF中的附加屬性,如Start-Class
(標(biāo)識應(yīng)用的實際主類)和Spring-Boot-Lib
(指向內(nèi)部依賴庫的位置)。
image.png image.png
啟動類加載器工作流程如下:
當(dāng)啟動器類加載器啟動時,它會根據(jù)MANIFEST.MF中的信息來組織類路徑,保證所有內(nèi)部的依賴庫都能正確地被加載。
加載器會區(qū)分出 BOOT-INF/classes
中的應(yīng)用程序類和 BOOT-INF/lib
下的依賴庫,分別處理并加入到類加載器的搜索路徑中。
加載器加載并執(zhí)行實際的Start-Class
,即應(yīng)用的主類,觸發(fā)Spring Boot框架的初始化和應(yīng)用的啟動流程。比如示例中的應(yīng)用主類:com.springboot.base.SpringBootBaseApplication
Spring Boot的啟動器和加載器機制有效地實現(xiàn)了對自包含jar包的管理和執(zhí)行,我們無需關(guān)心復(fù)雜的類路徑配置和依賴加載,只需通過一個簡單的命令即可啟動一個完整、獨立運行的應(yīng)用程序。
內(nèi)嵌Web容器 Spring Boot的一大特色就是能夠無縫整合并內(nèi)嵌多種輕量級Web容器,比如:Apache Tomcat
、Jetty
、Undertow
以及Reactor Netty
(對于響應(yīng)式編程模型)。內(nèi)嵌Web容器的引入極大地簡化了Web應(yīng)用的部署流程,我們不再需要在本地或服務(wù)器上獨立安裝和配置Web服務(wù)器(比如以前還要在本地安裝tomcat)。
當(dāng)Spring Boot應(yīng)用引入了spring-boot-starter-web
依賴時,默認情況下會自動配置并啟動一個內(nèi)嵌的Web容器。在Spring Boot啟動的過程中,內(nèi)嵌容器作為應(yīng)用的一部分被初始化并綁定到特定端口上,以便對外提供HTTP服務(wù)。
Spring Boot內(nèi)嵌web容器的優(yōu)點在于簡化部署,通過將Web容器內(nèi)置于應(yīng)用中,只需分發(fā)單一的JAR文件,就能在干凈的環(huán)境中運行應(yīng)用,避免了與現(xiàn)有Web服務(wù)器版本沖突或配置不當(dāng)?shù)葐栴};同時加快了啟動速度,尤其在開發(fā)和測試階段,實現(xiàn)近乎即時的熱重啟;提高了應(yīng)用的穩(wěn)定性,因為開發(fā)環(huán)境和生產(chǎn)環(huán)境使用相同的Web容器,降低了因環(huán)境差異導(dǎo)致的問題;此外,雖然容器是內(nèi)嵌的,但仍然可以進行全面的配置調(diào)整,如端口、連接數(shù)、SSL設(shè)置等,以滿足不同場景的需求。通過內(nèi)嵌Web容器,Spring Boot真正實現(xiàn)了“開箱即用”的理念。
自動配置與類路徑掃描 Spring Boot的核心特性之一就是其強大的自動配置能力,它允許應(yīng)用在幾乎零配置的情況下快速啟動并運行。
當(dāng)應(yīng)用啟動時,Spring Boot會讀取resource/META-INF/spring.factories
文件,該文件列出了所有可用的自動配置類。當(dāng)它檢測到應(yīng)用環(huán)境中對應(yīng)的自動配置類就會生效,通過@Configuration
注解的類創(chuàng)建并注冊Bean到Spring容器中,從而實現(xiàn)Bean的自動裝配。
這里說明下,在springboot3.x以后,就不在從resource/META-INF/spring.factories讀取自動配置類了,而是從org.springframework.boot.autoconfigure.AutoConfiguration.imports中讀取,這一點請參考文章:華為二面:SpringBoot如何自定義Starter ?
并且Spring Boot還采用條件注解(如@ConditionalOnClass
、@ConditionalOnMissingBean
等)來智能判斷何時應(yīng)用特定的配置。這些注解可以根據(jù)類路徑中是否存在特定類、系統(tǒng)屬性或環(huán)境變量的值等因素,決定是否應(yīng)該激活某個自動配置類。這意味著只有當(dāng)滿足特定條件時,相應(yīng)的Bean才會被創(chuàng)建和注入。
而對于應(yīng)用主類則是用@SpringBootApplication
注解標(biāo)識。@SpringBootApplication
是一個復(fù)合注解,包含了@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
三個注解的功能。其中
@SpringBootConfiguration
是一個Spring配置類,可以替代@Configuration
注解,聲明當(dāng)前類是Spring配置類,里面包含了一系列@Bean
方法或@ConfigurationProperties
等配置。
@EnableAutoConfiguration
啟用自動配置特性,告訴Spring Boot根據(jù)應(yīng)用類路徑中的依賴來自動配置Bean。Spring Boot會根據(jù)類路徑掃描的結(jié)果,智能地決定哪些自動配置類應(yīng)當(dāng)生效。
@ComponentScan
會自動掃描和管理Spring組件,包括@Service、@Repository、@Controller和@Component等注解標(biāo)注的類。通過該注解,Spring Boot能自動發(fā)現(xiàn)和管理應(yīng)用中的各個組件,并將其注冊為Spring容器中的Bean。
通過上述機制,Spring Boot能夠智能識別項目依賴、自動配置Bean,并結(jié)合類路徑掃描確保所有相關(guān)的組件和服務(wù)都被正確地初始化和管理,我們就可以專注于業(yè)務(wù)邏輯的開發(fā),而不必過多考慮基礎(chǔ)設(shè)施層面的配置問題。
總結(jié) Spring Boot 應(yīng)用程序被打包成的jar包之所以可以直接通過 java -jar
命令運行,是因為Spring Boot在構(gòu)建過程中做了一些特殊的設(shè)計和配置。具體原因:
Fat/Uber JAR : Spring Boot使用maven插件spring-boot-maven-plugin
(或Gradle對應(yīng)的插件)將項目及其所有依賴項打包成一個單一的、自包含的jar文件,通常稱為“Fat JAR”或“Uber JAR”。這意味著不僅包含了自己的類文件,還包含了運行應(yīng)用所需的所有第三方庫。
Manifest.MF : 在打包過程中,此插件會修改MANIFEST.MF文件,這是jar包中的一個元數(shù)據(jù)文件。在MANIFEST.MF中,特別指定了Main-Class
屬性,該屬性指向Spring Boot的一個內(nèi)置的啟動類(如org.springframework.boot.loader.JarLauncher
),這個啟動器類知道如何正確啟動Spring Boot應(yīng)用程序。
嵌入式Servlet容器 :Spring Boot默認集成了諸如Tomcat、Jetty或Undertow等嵌入式Web容器,使得無需外部服務(wù)器環(huán)境也能運行Web應(yīng)用。
啟動器類加載器 :當(dāng)通過java -jar
運行Spring Boot應(yīng)用時,JVM會根據(jù)MANIFEST.MF中的Main-Class
找到并運行指定的啟動器類。這個啟動器類加載器能夠解壓并加載內(nèi)部的依賴庫,并定位到實際的應(yīng)用主類(在spring-boot-starter-parent
或@SpringBootApplication
注解標(biāo)記的類),進而執(zhí)行其main
方法。
類路徑掃描和自動配置 :Spring Boot應(yīng)用通過特定的類路徑掃描機制和自動配置功能,能夠在啟動時識別出應(yīng)用所依賴的服務(wù)和組件,并自動配置它們,大大簡化了傳統(tǒng)Java應(yīng)用的配置和部署過程。
Spring Boot通過精心設(shè)計的打包流程和啟動器類,使得生成的jar包可以直接作為一個獨立的應(yīng)用程序運行,極大地簡化了部署和運維復(fù)雜度。
版權(quán)聲明:本文系公眾號 "碼農(nóng)Academy" 原創(chuàng),轉(zhuǎn)載、引用本文內(nèi)容請注明出處
該文章在 2024/4/9 23:55:55 編輯過