SpringBoot啟動(dòng)速度優(yōu)化的5個(gè)技巧分享
Spring Boot 啟動(dòng)慢的痛點(diǎn)
作為一名后端開發(fā),日常工作中經(jīng)常與 Spring Boot 框架打交道。在項(xiàng)目開發(fā)初期,Spring Boot 憑借其 “約定優(yōu)于配置” 的理念,確實(shí)極大地提高了開發(fā)效率,快速搭建起基礎(chǔ)框架。然而,隨著項(xiàng)目的不斷迭代和功能的持續(xù)增加,一個(gè)令人頭疼的問題逐漸凸顯出來(lái) ——Spring Boot 的啟動(dòng)速度越來(lái)越慢。
在本地開發(fā)環(huán)境中,每次修改代碼后重啟項(xiàng)目,都需要漫長(zhǎng)的等待時(shí)間。有時(shí)候只是修改了一個(gè)小功能,想要快速驗(yàn)證效果,卻不得不花費(fèi)數(shù)分鐘等待項(xiàng)目啟動(dòng)完成,這嚴(yán)重打斷了開發(fā)思路,降低了開發(fā)效率。就好比你在寫一篇文章,思路正流暢的時(shí)候,卻因?yàn)殡娔X反應(yīng)慢,每次敲幾個(gè)字都要卡頓一下,極大地影響了創(chuàng)作的連貫性。
在生產(chǎn)環(huán)境中,啟動(dòng)慢的問題更是帶來(lái)了諸多隱患。每次上線新版本,較長(zhǎng)的啟動(dòng)時(shí)間意味著服務(wù)不可用的窗口期變長(zhǎng),這期間可能會(huì)影響用戶的正常使用,導(dǎo)致業(yè)務(wù)損失。而且,在一些對(duì)服務(wù)響應(yīng)速度要求極高的場(chǎng)景下,如電商大促期間,啟動(dòng)慢可能會(huì)成為壓垮系統(tǒng)的最后一根稻草,引發(fā)連鎖反應(yīng),造成嚴(yán)重的后果。
曾經(jīng)參與過一個(gè)電商項(xiàng)目,在一次促銷活動(dòng)前夕進(jìn)行系統(tǒng)升級(jí)。由于項(xiàng)目中 Spring Boot 應(yīng)用啟動(dòng)緩慢,新服務(wù)遲遲無(wú)法上線,差點(diǎn)錯(cuò)過活動(dòng)開始時(shí)間,幸好及時(shí)優(yōu)化才避免了一場(chǎng)危機(jī)。這件事讓我深刻認(rèn)識(shí)到,Spring Boot 啟動(dòng)慢絕不是一個(gè)可以忽視的小問題,而是關(guān)系到開發(fā)效率、系統(tǒng)穩(wěn)定性和業(yè)務(wù)發(fā)展的關(guān)鍵痛點(diǎn),必須要找到有效的解決辦法。
優(yōu)化點(diǎn)一:減少業(yè)務(wù)初始化
問題分析
在 Spring Boot 應(yīng)用啟動(dòng)過程中,業(yè)務(wù)初始化操作是耗時(shí)的重要環(huán)節(jié)。以數(shù)據(jù)庫(kù)連接為例,建立與數(shù)據(jù)庫(kù)的連接需要進(jìn)行網(wǎng)絡(luò)通信、身份驗(yàn)證以及資源分配等一系列復(fù)雜操作。假設(shè)我們使用的是 MySQL 數(shù)據(jù)庫(kù),在初始化時(shí),Spring Boot 會(huì)根據(jù)配置文件中的信息,加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),然后嘗試與 MySQL 服務(wù)器建立 TCP 連接。這個(gè)過程中,如果網(wǎng)絡(luò)不穩(wěn)定或者服務(wù)器負(fù)載較高,連接的建立可能會(huì)花費(fèi)數(shù)秒甚至更長(zhǎng)時(shí)間。
再看 Redis 連接,同樣需要進(jìn)行網(wǎng)絡(luò)握手、配置參數(shù)協(xié)商等步驟。當(dāng)應(yīng)用程序需要與 Redis 集群進(jìn)行交互時(shí),初始化過程還包括對(duì)集群節(jié)點(diǎn)信息的獲取和管理,這無(wú)疑會(huì)進(jìn)一步增加啟動(dòng)的時(shí)間開銷。
除了這些外部資源連接,項(xiàng)目中一些復(fù)雜的業(yè)務(wù)邏輯初始化也會(huì)帶來(lái)問題。比如,在一個(gè)電商系統(tǒng)中,啟動(dòng)時(shí)可能需要加載大量的商品數(shù)據(jù)到內(nèi)存緩存中,以提高后續(xù)查詢的效率。如果商品數(shù)據(jù)量龐大,這個(gè)加載過程會(huì)占用大量的 CPU 和內(nèi)存資源,導(dǎo)致啟動(dòng)速度大幅下降。
優(yōu)化方案
減少不必要的依賴是優(yōu)化的關(guān)鍵一步。在項(xiàng)目的依賴管理文件(如 Maven 的 pom.xml 或 Gradle 的 build.gradle)中,仔細(xì)檢查每個(gè)依賴項(xiàng)。有些依賴可能是在開發(fā)過程中臨時(shí)引入用于測(cè)試或?qū)嶒?yàn)性功能的,但在正式環(huán)境中不再需要。例如,在開發(fā)階段,為了方便調(diào)試數(shù)據(jù)庫(kù)操作,可能引入了一個(gè)數(shù)據(jù)庫(kù)管理工具的依賴,但在生產(chǎn)環(huán)境中,這個(gè)依賴并不會(huì)被實(shí)際使用,就可以將其去除。
對(duì)于一些非關(guān)鍵的初始化邏輯,將其改為異步執(zhí)行能顯著加快啟動(dòng)速度。Spring 框架提供了強(qiáng)大的異步支持,使用 @Async 注解可以輕松實(shí)現(xiàn)這一目標(biāo)。首先,在 Spring Boot 的主啟動(dòng)類上添加 @EnableAsync 注解,開啟異步功能。然后,在需要異步執(zhí)行的初始化方法上添加 @Async 注解。比如,在一個(gè)郵件發(fā)送服務(wù)中,啟動(dòng)時(shí)可能需要初始化一些郵件模板和配置信息,這些操作并不影響應(yīng)用的核心功能,可以將其異步化:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async
public void initEmailConfig() {
// 模擬郵件配置初始化操作,如加載模板文件、設(shè)置郵件服務(wù)器地址等
try {
Thread.sleep(2000);
System.out.println("郵件配置初始化完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
這樣,在應(yīng)用啟動(dòng)時(shí),initEmailConfig 方法會(huì)被提交到一個(gè)異步線程池中執(zhí)行,主線程不會(huì)被阻塞,從而加快了啟動(dòng)速度。
優(yōu)化點(diǎn)二:延遲初始化
原理介紹
在 Spring Boot 2.2 版本后,引入了一個(gè)非常實(shí)用的屬性 ——spring.main.lazy-initialization。這個(gè)屬性就像是一個(gè)聰明的調(diào)度員,它的作用是延遲所有 Bean 的初始化。
在傳統(tǒng)的 Spring Boot 啟動(dòng)過程中,就好比一場(chǎng)熱鬧的聚會(huì),所有賓客(Bean)都會(huì)在聚會(huì)一開始就全部到場(chǎng),不管他們是否馬上要參與活動(dòng)。這就導(dǎo)致了在啟動(dòng)時(shí),需要花費(fèi)大量時(shí)間來(lái)初始化每一個(gè) Bean,即使有些 Bean 在啟動(dòng)后的很長(zhǎng)一段時(shí)間內(nèi)都不會(huì)被使用。
而有了 spring.main.lazy-initialization 屬性后,情況就大不一樣了。它讓賓客們(Bean)不再需要在聚會(huì)一開始就全部到場(chǎng),而是等到有需要的時(shí)候才過來(lái)。這樣一來(lái),在啟動(dòng)階段,就只需要處理那些真正急需的 Bean,大大減少了啟動(dòng)時(shí)的工作量,從而加快了啟動(dòng)速度。
實(shí)際應(yīng)用
使用這個(gè)優(yōu)化點(diǎn)非常簡(jiǎn)單,只需要在 application.yml 文件中添加如下配置:
spring:
main:
lazy-initialization: true配置完成后,Spring Boot 應(yīng)用在啟動(dòng)時(shí),只會(huì)初始化一些必要的基礎(chǔ)設(shè)施 Bean,而其他業(yè)務(wù) Bean 會(huì)被延遲初始化。當(dāng)?shù)谝淮卧L問某個(gè)需要特定 Bean 的接口或功能時(shí),對(duì)應(yīng)的 Bean 才會(huì)被初始化。
需要注意的是,由于這種延遲初始化機(jī)制,首次訪問某些接口時(shí),可能會(huì)因?yàn)?Bean 的初始化而導(dǎo)致響應(yīng)速度變慢,就像聚會(huì)中臨時(shí)邀請(qǐng)賓客,賓客需要時(shí)間趕過來(lái)一樣。但后續(xù)的訪問就會(huì)恢復(fù)正常速度,因?yàn)?Bean 已經(jīng)初始化好了。在一個(gè)電商項(xiàng)目中,開啟延遲初始化后,啟動(dòng)時(shí)間從原來(lái)的 10 秒縮短到了 6 秒,雖然首次訪問商品列表接口時(shí),響應(yīng)時(shí)間增加了 1 秒左右,但在后續(xù)的操作中,整體的系統(tǒng)性能并沒有受到影響,而啟動(dòng)速度的提升卻帶來(lái)了開發(fā)和部署效率的顯著提高 。
優(yōu)化點(diǎn)三:Spring Context Indexer
功能解析
從 Spring5 版本開始,Spring 框架提供了一個(gè)非常實(shí)用的功能 ——spring-context-indexer。在大型 Spring Boot 項(xiàng)目中,類的數(shù)量眾多,Spring 在啟動(dòng)時(shí)進(jìn)行類掃描的工作量巨大,這是導(dǎo)致啟動(dòng)緩慢的一個(gè)重要原因。就好比在一個(gè)大型圖書館里找一本書,如果沒有索引,你需要一本本去查找,效率非常低。
spring-context-indexer 的作用就是提前生成 @ComponentScan 的掃描索引,就像給圖書館的書籍制作了詳細(xì)的索引目錄。在項(xiàng)目編譯階段,它會(huì)收集所有帶有 Spring 模式注解(如 @Component、@Service、@Repository 等)的類信息,并將這些信息記錄在一個(gè)特定的索引文件中。當(dāng) Spring Boot 應(yīng)用啟動(dòng)執(zhí)行 @ComponentScan 掃描類時(shí),不再需要遍歷整個(gè)類路徑去查找這些注解類,而是直接讀取這個(gè)索引文件,快速定位到需要的類,從而大大提高了掃描速度,減少了啟動(dòng)時(shí)間。
使用步驟
使用 spring-context-indexer 功能非常簡(jiǎn)單,首先需要在項(xiàng)目的依賴管理文件中導(dǎo)入相關(guān)依賴。如果使用 Maven,在 pom.xml 文件中添加如下依賴:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<optional>true</optional>
</dependency>如果使用 Gradle,在 build.gradle 文件中添加:
annotationProcessor 'org.springframework:spring-context-indexer'
添加依賴后,在 Spring Boot 的啟動(dòng)類上添加 @Indexed 注解:
import org.springframework.context.annotation.Indexed;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Indexed
@SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
完成上述操作后,重新編譯項(xiàng)目。在編譯打包之后,會(huì)在項(xiàng)目的 META-INT 目錄下生成一個(gè) spring.components 文件。這個(gè)文件就是索引文件,它記錄了項(xiàng)目中所有被 Spring 管理的組件信息。當(dāng)應(yīng)用啟動(dòng)時(shí),Spring 會(huì)優(yōu)先讀取這個(gè)文件,快速完成組件掃描,大大提高啟動(dòng)速度。在一個(gè)擁有上千個(gè)類的大型電商項(xiàng)目中,使用 spring-context-indexer 后,啟動(dòng)時(shí)間縮短了約 30%,效果十分顯著 。
優(yōu)化點(diǎn)四:關(guān)閉 JMX
默認(rèn)設(shè)置
在 Spring Boot 2.2.X 版本以下,默認(rèn)情況下,JMX(Java Management Extensions)是開啟的。JMX 是 Java 提供的一套標(biāo)準(zhǔn) API,它就像是一個(gè)全面的監(jiān)控室,可以用于監(jiān)控 JVM 狀態(tài),如內(nèi)存使用情況、線程活動(dòng)、垃圾回收(GC)等,還能管理應(yīng)用組件,比如啟停服務(wù)、調(diào)整參數(shù)等。通過 JMX,我們可以將應(yīng)用中的組件以 MBean(Managed Bean)的形式暴露出來(lái),方便進(jìn)行管理和監(jiān)控 。
關(guān)閉方法
如果在項(xiàng)目中不需要使用 JMX 進(jìn)行監(jiān)控和管理,那么可以手動(dòng)將其關(guān)閉,這有助于提升 Spring Boot 應(yīng)用的啟動(dòng)速度。在 application.yml 文件中添加如下配置即可關(guān)閉 JMX:
spring:
jmx:
enabled: false關(guān)閉 JMX 之所以能提升啟動(dòng)速度,是因?yàn)樵趩?dòng)過程中,Spring Boot 不再需要進(jìn)行與 JMX 相關(guān)的初始化操作,如創(chuàng)建 MBeanServer、注冊(cè) MBeans 等。這些操作雖然在功能上很強(qiáng)大,但對(duì)于一些不需要 JMX 功能的項(xiàng)目來(lái)說,卻是額外的負(fù)擔(dān)。就好比一輛汽車,原本安裝了一些特殊的設(shè)備用于專業(yè)檢測(cè),但如果我們只是日常駕駛,這些設(shè)備不僅占用空間,還可能增加啟動(dòng)時(shí)的準(zhǔn)備時(shí)間。關(guān)閉 JMX 就像是卸下了這些不必要的設(shè)備,讓 Spring Boot 應(yīng)用能夠輕裝上陣,更快地完成啟動(dòng)過程 。
優(yōu)化點(diǎn)五:關(guān)閉分層編譯
編譯原理
從 JDK8 版本開始,Java 默認(rèn)打開了多層編譯,也就是分層編譯機(jī)制。這一機(jī)制是 JVM 實(shí)現(xiàn) “啟動(dòng)速度” 與 “運(yùn)行效率” 平衡的核心機(jī)制 。在分層編譯中,涉及到兩個(gè)重要的編譯器:Tier3 也就是 C1 編譯器,以及 Tier4 即 C2 編譯器。
當(dāng) Java 程序開始運(yùn)行時(shí),代碼首先會(huì)被解釋執(zhí)行。在這個(gè)過程中,JVM 會(huì)監(jiān)控字節(jié)碼的執(zhí)行頻率,當(dāng)發(fā)現(xiàn)某段代碼(熱點(diǎn)代碼)被頻繁調(diào)用時(shí),就會(huì)觸發(fā)編譯操作。如果方法解釋編譯達(dá)到 2000 次以后,就會(huì)進(jìn)行 C1 編譯。C1 編譯器編譯速度快,它主要做一些基礎(chǔ)的優(yōu)化,如方法內(nèi)聯(lián)、常量傳播、基本塊調(diào)度等,生成的代碼體積小、編譯延遲低,適合啟動(dòng)階段或?qū)憫?yīng)敏感的場(chǎng)景,能快速提升代碼的執(zhí)行速度。
而當(dāng) C1 編譯后的代碼執(zhí)行次數(shù)達(dá)到 15000 次時(shí),就會(huì)觸發(fā) C2 編譯。C2 編譯器編譯速度較慢,但它會(huì)進(jìn)行大量深度優(yōu)化,如激進(jìn)預(yù)測(cè)、循環(huán)優(yōu)化、鎖消除、逃逸分析、循環(huán)展開、向量化等,能最大化程序的峰值性能,生成的代碼效率極高,非常適合長(zhǎng)時(shí)間運(yùn)行的服務(wù)端應(yīng)用 。
(優(yōu)化操作
如果我們希望提高 Spring Boot 應(yīng)用的啟動(dòng)速度,可以通過命令使用 C1 編譯器,同時(shí)配合關(guān)閉字節(jié)碼驗(yàn)證的命令。在啟動(dòng)應(yīng)用時(shí),可以添加如下 JVM 參數(shù):
-XX:TieredStopAtLevel=1 -XX:-VerifyBytecode
其中,\-XX:TieredStopAtLevel=1表示只使用 C1 編譯器,停止在 Tier1 層,不進(jìn)行 C2 編譯;\-XX:\-VerifyBytecode表示關(guān)閉字節(jié)碼驗(yàn)證。字節(jié)碼驗(yàn)證是 Java 類加載過程中的一個(gè)重要環(huán)節(jié),它會(huì)檢查字節(jié)碼是否符合 Java 虛擬機(jī)規(guī)范,是否存在安全隱患等。關(guān)閉字節(jié)碼驗(yàn)證可以節(jié)省類加載時(shí)間,從而加快應(yīng)用啟動(dòng)速度。
不過,需要特別強(qiáng)調(diào)的是,這種優(yōu)化方式雖然能顯著提高啟動(dòng)速度,但存在一定風(fēng)險(xiǎn)。關(guān)閉字節(jié)碼驗(yàn)證意味著跳過了對(duì)字節(jié)碼的安全性和合規(guī)性檢查,如果代碼中存在錯(cuò)誤或安全漏洞,可能在運(yùn)行時(shí)才被發(fā)現(xiàn),導(dǎo)致應(yīng)用出現(xiàn)異常甚至崩潰。因此,這種優(yōu)化方式盡量不要在線上環(huán)境使用,一般只在開發(fā)和測(cè)試環(huán)境中,對(duì)啟動(dòng)速度有迫切需求且能確保代碼質(zhì)量的情況下采用 。
以上就是SpringBoot啟動(dòng)速度優(yōu)化的5個(gè)技巧分享的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot啟動(dòng)優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea的language?level默認(rèn)為5的解決方案
文章介紹了在IDEA中修改項(xiàng)目語(yǔ)言級(jí)別(language?level)時(shí)遇到的問題,以及解決方法,方法1是通過在父pom文件中添加配置來(lái)解決,方法2是修改maven的settings.xml文件2025-12-12
java實(shí)現(xiàn)圖片任意角度旋轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)圖片任意角度旋轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
idea?intellij快速修復(fù)if語(yǔ)句缺少大括號(hào)的問題
這篇文章主要介紹了idea?intellij快速修復(fù)if語(yǔ)句缺少大括號(hào)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Java Socket實(shí)現(xiàn)多線程通信功能示例
這篇文章主要介紹了Java Socket實(shí)現(xiàn)多線程通信功能,結(jié)合具體實(shí)例形式較為詳細(xì)的分析了java多線程通信的原理及客戶端、服務(wù)器端相應(yīng)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06
Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn))
Token最常見的應(yīng)用場(chǎng)景之一就是身份驗(yàn)證,本文主要介紹了Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn)),具有一定的參考價(jià)值,感興趣的可以來(lái)了解一下2024-04-04
Java代碼性能測(cè)試實(shí)戰(zhàn)之ContiPerf安裝使用
這篇文章主要為大家介紹了Java代碼性能測(cè)試實(shí)戰(zhàn)之ContiPerf安裝使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Java中SPI機(jī)制的實(shí)現(xiàn)詳解
SPI(Service?Provider?Interface),是?JDK?內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,可以用來(lái)啟用框架擴(kuò)展和替換組件,下面我們就來(lái)看看Java中SPI機(jī)制的具體實(shí)現(xiàn)2024-01-01

