SpringBoot 啟動加速:優化與定制化干預技術解析
隨著微服務架構的普及,SpringBoot 已成為構建高效、可擴展的應用程序的首選框架之一。然而,SpringBoot 作為一個全功能框架,雖然提供了豐富的特性和便捷的開發體驗,但在啟動速度上,尤其是對于大規模應用,往往表現不盡如人意。啟動時間過長不僅影響開發效率,還可能影響生產環境中系統的響應速度,甚至在某些情況下影響用戶體驗。
為了應對這一問題,許多開發者開始尋求 SpringBoot 啟動優化 的解決方案,力求在保證應用功能的同時,最大程度地減少啟動延遲。本文將深入探討如何通過 干預與優化 兩大策略,幫助開發者加速 SpringBoot 應用的啟動時間。我們將從實際應用角度出發,介紹一系列行之有效的優化方法,并提供可操作性的指導,助力開發者提升 SpringBoot 啟動效率,從而在大規模分布式環境中保持高效的開發和運行。
無論你是初次接觸 SpringBoot,還是已經有一定經驗的開發者,本文的內容都將為你提供重要的實踐價值,幫助你在現代應用開發中實現更優的性能和用戶體驗。
一、SpringBoot啟動配置原理簡述
本內容直接查看分析SpringBoot啟動配置原理_單塊系統_張彥峰ZYF的博客-CSDN博客對應詳細講解。因為本期的各種優化干預手段都依賴其基本原理,建議在使用前進行閱讀。
二、SpringBoot啟動過程干預
Spring Boot啟動過程中我們可以實現以下干預工作:
-
修改Spring Boot默認的配置屬性。使用@ConfigurationProperties和@EnableConfigurationProperties注解,可以獲取和修改Spring Boot的配置屬性。
-
加載配置文件。Spring Boot會自動加載application.properties或application.yml等配置文件,我們可以在啟動時加載其他配置文件。
-
自定義bean。我們可以通過@Component注解創建自定義的bean以及其他的@SpringBootAnnotation注解,來實現更靈活的配置和自動化初始化。
-
執行一些初始化邏輯。我們可以對應用程序的數據庫、緩存、MQ等進行初始化,例如創建數據源、初始化緩存等,以確保應用程序正常運行,并且可以通過ApplicationRunner和CommandLineRunner等干預代碼的方式執行這些初始化邏輯。
-
執行一些后置操作。在Spring Boot應用程序停止后執行一些清理工作,例如關閉數據源、釋放緩存等。
這些干預步驟可以在Spring Boot應用程序啟動和停止完成后進行,從而實現更靈活的配置和初始化。
(一)ApplicationContextInitializer擴展
通過實現ApplicationContextInitializer接口,我們可以在ApplicationContext創建之前對其進行一些定制化的修改。這個接口定義了一個initialize方法,接受一個ConfigurableApplicationContext對象作為參數,我們可以在這個方法中對這個對象進行修改和配置。
可以通過ApplicationContextInitializer實現以下擴展任務:
-
修改Spring Boot默認的environment屬性。使用
configurableApplicationContext.getEnvironment()方法獲取到environment對象,從而修改環境變量,例如添加自定義配置文件路徑。 -
添加自定義的PropertySource。使用
environment.getPropertySources().addLast(propertySource)方法,可以添加自定義的屬性源,從而實現更靈活的配置。 -
注冊自定義bean。使用
configurableApplicationContext.getBeanFactory().registerSingleton(beanName, bean)方法,可以注冊自定義的bean,從而實現更靈活的依賴注入。
可以添加自定義的ApplicationContextInitializer實現類,從而擴展應用程序的初始化邏輯。通過實現ApplicationContextInitializer接口,可以在Spring Boot應用程序啟動之前對應用程序進行一些初始化定制化的操作,從而滿足開發者對應用程序的特殊需求。
修改Spring Boot默認的environment屬性
修改Spring Boot默認的environment屬性,例如添加自定義配置文件路徑,可以通過實現ApplicationContextInitializer接口來完成。
-
package org.zyf.javabasic.springextend.runext;
-
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-
import org.springframework.context.ApplicationContextInitializer;
-
import org.springframework.context.ConfigurableApplicationContext;
-
import org.springframework.core.env.ConfigurableEnvironment;
-
import org.springframework.core.env.PropertySource;
-
import org.springframework.core.io.support.ResourcePropertySource;
-
-
import java.io.IOException;
-
-
/**
-
* @author yanfengzhang
-
* @description 修改Spring Boot默認的environment屬性。使用configurableApplicationContext.getEnvironment()方法獲取到environment對象,從而修改環境變量,例如添加自定義配置文件路徑。
-
* @date 2021/2/23 23:48
-
*/
-
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
-
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
-
-
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
-
// 添加自定義配置文件路徑
-
try {
-
ObjectMapper objectMapper = new ObjectMapper();
-
System.out.println("InterveneApplicationContextInitializer initialize :" + configurableApplicationContext);
-
environment.getPropertySources().addFirst(new ResourcePropertySource("classpath:zyftest.properties"));
-
System.out.println("InterveneApplicationContextInitializer initialize add FirstResourcePropertySource classpath:zyftest.properties");
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
在上面的代碼中,我們通過getEnvironment()方法獲取到ConfigurableEnvironment對象,然后通過getPropertySources()方法獲取到屬性源,使用addFirst()方法將自定義的custom.properties文件的PropertySource添加到屬性源的首位。這樣,在應用程序啟動時,就會首先加載custom.properties文件,從而實現了自定義的配置。
需要注意的是,上述代碼中的InterveneApplicationContextInitializer需要被注冊才能生效。可以通過在src/main/resources/META-INF/spring.factories文件中指定注冊項的方式來注冊:
-
org.springframework.context.ApplicationContextInitializer=
-
org.zyf.javabasic.springextend.runext.InterveneApplicationContextInitializer
添加自定義的PropertySource
添加自定義的PropertySource,可以通過實現ApplicationContextInitializer接口來完成。添加自定義的PropertySource:
-
package org.zyf.javabasic.springextend.runext;
-
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-
import org.springframework.context.ApplicationContextInitializer;
-
import org.springframework.context.ConfigurableApplicationContext;
-
import org.springframework.core.env.ConfigurableEnvironment;
-
import org.springframework.core.env.PropertySource;
-
import org.springframework.core.io.support.ResourcePropertySource;
-
-
import java.io.IOException;
-
-
/**
-
* @author yanfengzhang
-
* @description 添加自定義的PropertySource。使用environment.getPropertySources().addLast(propertySource)方法,可以添加自定義的屬性源,從而實現更靈活的配置。
-
* @date 2021/2/23 23:48
-
*/
-
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
-
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
-
// 添加自定義的PropertySource
-
PropertySource<?> propertySource = new MyPropertySource("myPropertySource");
-
environment.getPropertySources().addLast(propertySource);
-
System.out.println("InterveneApplicationContextInitializer initialize add PropertySource myPropertySource");
-
}
-
-
// 自定義PropertySource
-
private static class MyPropertySource extends PropertySource<String> {
-
private static final String MY_PROPERTY_SOURCE_KEY = "my.property.source.key";
-
-
public MyPropertySource(String name) {
-
super(name);
-
}
-
-
-
public Object getProperty(String name) {
-
if (MY_PROPERTY_SOURCE_KEY.equals(name)) {
-
return "myPropertySourceValue";
-
}
-
return null;
-
}
-
}
-
}
在上面的代碼中,我們新建了一個名為MyPropertySource的自定義PropertySource,然后在initialize方法中使用environment.getPropertySources().addLast(propertySource)方法將其添加到Spring環境中。MyPropertySource中實現了一個用于獲取屬性的getProperty方法,在這個方法中,我們指定了一個名為my.property.source.key的屬性及其對應的值,這樣就可以通過@Value("${my.property.source.key}")的方式在應用程序中獲取到它的值了。
需要注意的是,上述代碼中的InterveneApplicationContextInitializer需要被注冊才能生效。具體如上部分展示,這里不在展示。
注冊自定義bean
注冊自定義Bean,可以通過實現ApplicationContextInitializer接口來完成。注冊自定義Bean:
-
package org.zyf.javabasic.springextend.runext;
-
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-
import org.springframework.context.ApplicationContextInitializer;
-
import org.springframework.context.ConfigurableApplicationContext;
-
import org.springframework.core.env.ConfigurableEnvironment;
-
import org.springframework.core.env.PropertySource;
-
import org.springframework.core.io.support.ResourcePropertySource;
-
-
import java.io.IOException;
-
-
/**
-
* @author yanfengzhang
-
* @description 注冊自定義bean。使用configurableApplicationContext.getBeanFactory().registerSingleton(beanName, bean)方法,可以注冊自定義的bean,從而實現更靈活的依賴注入。
-
* @date 2021/2/23 23:48
-
*/
-
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
-
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
-
-
ConfigurableListableBeanFactory beanFactory = configurableApplicationContext.getBeanFactory();
-
// 注冊自定義Bean
-
MyBean myBean = new MyBean();
-
beanFactory.registerSingleton("myBean", myBean);
-
System.out.println("InterveneApplicationContextInitializer initialize registerSingleton myBean");
-
}
-
-
// 自定義Bean
-
private static class MyBean {
-
private String name = "myBean";
-
-
public String getName() {
-
return name;
-
}
-
}
-
}
在上面的代碼中,我們新建了一個名為MyBean的自定義Bean,然后在initialize方法中使用beanFactory.registerSingleton("myBean", myBean)方法將其注冊到Spring應用程序上下文中,"myBean"是注冊的bean名稱,myBean是實際的類實例對象。
需要注意的是,上述代碼中的InterveneApplicationContextInitializer需要被注冊才能生效。具體如上部分展示,這里不在展示。
(二)SpringApplicationRunListener擴展
SpringApplicationRunListener是Spring Boot的一個事件監聽器,用于在應用程序啟動和停止時執行一些操作。可能需要自定義SpringApplicationRunListener來執行某些特定操作。
首先,需要實現SpringApplicationRunListener接口:
-
package org.zyf.javabasic.springextend.runext;
-
-
import org.springframework.boot.SpringApplication;
-
import org.springframework.boot.SpringApplicationRunListener;
-
import org.springframework.context.ConfigurableApplicationContext;
-
import org.springframework.core.env.ConfigurableEnvironment;
-
-
/**
-
* @author yanfengzhang
-
* @description SpringApplicationRunListener是Spring Boot的一個事件監聽器,用于在應用程序啟動和停止時執行一些操作。
-
* 可能需要自定義SpringApplicationRunListener來執行某些特定操作。
-
* 下面是一個示例,演示如何擴展SpringApplicationRunListener以添加自定義操作
-
* @date 2021/2/23 23:44
-
*/
-
public class IntervenRunListener implements SpringApplicationRunListener {
-
-
private final SpringApplication application;
-
-
private final String[] args;
-
-
public IntervenRunListener(SpringApplication application, String[] args) {
-
this.application = application;
-
this.args = args;
-
}
-
-
-
public void starting() {
-
System.out.println("IntervenRunListener starting");
-
}
-
-
-
public void environmentPrepared(ConfigurableEnvironment environment) {
-
System.out.println("IntervenRunListener environmentPrepared");
-
}
-
-
-
public void contextPrepared(ConfigurableApplicationContext context) {
-
System.out.println("IntervenRunListener contextPrepared");
-
}
-
-
-
public void contextLoaded(ConfigurableApplicationContext context) {
-
System.out.println("IntervenRunListener contextLoaded");
-
}
-
-
-
public void started(ConfigurableApplicationContext context) {
-
System.out.println("IntervenRunListener started");
-
}
-
-
-
public void running(ConfigurableApplicationContext context) {
-
System.out.println("IntervenRunListener running");
-
}
-
-
-
public void failed(ConfigurableApplicationContext context, Throwable exception) {
-
System.out.println("IntervenRunListener failed");
-
}
-
}
在上述代碼中,我們新建了一個名為IntervenRunListener的自定義SpringApplicationRunListener,并在starting()、environmentPrepared()、contextPrepared()、contextLoaded()、started()、running()和failed()方法中添加了自定義操作。
接下來,我們需要告訴Spring Boot使用這個自定義的SpringApplicationRunListener,上述代碼中的IntervenRunListener需要被注冊才能生效。可以通過在src/main/resources/META-INF/spring.factories文件中指定注冊項的方式來注冊:
-
org.springframework.boot.SpringApplicationRunListener=
-
org.zyf.javabasic.springextend.runext.IntervenRunListener
(三)ApplicationRunner擴展
ApplicationRunner是Spring Boot提供的一種擴展點,它允許在Spring Boot應用程序啟動時執行一些預配置操作。這些操作可以包括預熱緩存,初始化數據庫連接等。
- 數據庫初始化:
ApplicationRunner可以用于執行數據庫初始化操作。例如,我們可以在應用程序啟動時創建數據庫表格,插入初始數據等操作。這對于確保數據庫的正確性和可用性非常有用,以及為整個應用程序提供更好的可維護性。 - 緩存預熱:緩存在應用程序中非常重要,它可以大大提高應用程序的性能。但是,由于緩存通常是“懶加載”的,所以在應用程序第一次使用它們時,需要花費一些時間來加載它們。使用
ApplicationRunner,我們可以在應用程序啟動后立即加載緩存,而不是等到應用程序第一次使用它們時加載。這可以大大減少應用程序響應時間,并提高用戶體驗。 - 環境檢查:為了確保應用程序能夠正常運行,我們需要檢查它所在的環境是否滿足應用程序的要求。例如,我們可能需要檢查數據庫是否可用,檢查文件系統是否可寫等。使用
ApplicationRunner,我們可以在應用程序啟動時立即執行這些檢查,并在應用程序無法正常運行時采取適當的措施,如打印警告或拋出異常。
ApplicationRunner可以用于執行任何需要在應用程序啟動時進行的操作。它為應用程序提供了一種簡單的擴展點,可以使我們輕松地實現預配置和初始化操作。
以下從緩存預熱和環境檢查給出簡單的代碼示例。
緩存預熱
在應用程序啟動后立即加載緩存,可以避免在應用程序第一次使用緩存時的延遲。以下是一個簡單的示例,演示如何使用ApplicationRunner在應用程序啟動時加載緩存:
-
package org.zyf.javabasic.springextend.runext;
-
-
import org.springframework.boot.ApplicationArguments;
-
import org.springframework.boot.ApplicationRunner;
-
import org.springframework.stereotype.Component;
-
-
/**
-
* @author yanfengzhang
-
* @description 演示如何使用ApplicationRunner在應用程序啟動時加載緩存
-
* @date 2021/2/23 23:13
-
*/
-
-
public class CacheInitiator implements ApplicationRunner {
-
-
public void run(ApplicationArguments args) throws Exception {
-
System.out.println("CacheInitiator cache deal!");
-
}
-
}
在應用程序啟動時,ApplicationRunner接口的run方法將被自動調用,從而將一些初始數據加載到緩存中。當應用程序需要訪問緩存時,它們可以立即從緩存中獲取數據,而不必等待它們被“懶加載”的時間。這將大大提高應用程序的性能和用戶體驗。
環境檢查
使用ApplicationRunner在應用程序啟動時執行環境檢查:
-
package org.zyf.javabasic.springextend.runext;
-
-
import org.springframework.boot.ApplicationArguments;
-
import org.springframework.boot.ApplicationRunner;
-
import org.springframework.stereotype.Component;
-
-
/**
-
* @author yanfengzhang
-
* @description 演示如何使用ApplicationRunner在應用程序啟動時執行環境檢查
-
* @date 2021/2/23 23:17
-
*/
-
-
public class EnvironmentChecker implements ApplicationRunner {
-
-
public void run(ApplicationArguments args) throws Exception {
-
// 檢查數據庫是否可用
-
System.out.println("EnvironmentChecker DatabaseConnection checkConnection! ");
-
-
// 檢查文件系統是否可寫
-
System.out.println("EnvironmentChecker FileStorage checkWriteAccess! ");
-
}
-
}
在應用程序啟動時,ApplicationRunner接口的run方法將被自動調用,從而執行環境檢查操作。當應用程序無法正常運行時,它們將拋出一個運行時異常,包含適當的錯誤消息,以幫助我們進行故障排除和修復操作。
(四)CommandLineRunner擴展
CommandLineRunner接口用于在應用程序啟動時執行一些命令行操作。這在調試應用程序、自動化部署、初始化系統配置等方面非常有用。
在較復雜的業務場景下,我們可以使用CommandLineRunner接口來擴展應用程序。
首先,我們可以將一些常用的業務邏輯封裝在命令行工具中,然后在應用程序啟動時通過執行這些命令來進行操作。例如,我們可以創建一個名為UserImportCommand的命令行工具,用于導入用戶數據到應用程序中。在應用程序啟動時,可以執行UserImportCommand來導入用戶數據。
其次,我們可以使用CommandLineRunner接口來定制化應用程序的啟動過程。例如,我們可以創建一個名為StartupTasks的類,并實現CommandLineRunner接口。在run方法中,我們可以執行任何我們需要在應用程序啟動時完成的任務,如加載配置文件、初始化緩存等。
最后,我們可以結合使用ApplicationRunner和CommandLineRunner來完成更復雜的任務。例如,我們可以創建一個名為InitializationRunner的類,實現ApplicationRunner和CommandLineRunner接口,并在其中執行所有初始化任務。這樣,在應用程序啟動時,不僅可以自動執行初始化任務,還可以通過命令行手動執行這些任務。
CommandLineRunner接口在業務擴展方面有著廣闊的應用前景,在實際業務場景中,可以根據自己的需求進行靈活應用和擴展。
應用舉例一:UserImportCommand命令行工具
使用CommandLineRunner接口創建一個命令行工具,用于導入用戶數據到應用程序中。
-
package org.zyf.javabasic.springextend.runext;
-
-
import com.google.common.collect.Lists;
-
import org.springframework.boot.CommandLineRunner;
-
import org.springframework.stereotype.Component;
-
import org.zyf.javabasic.common.User;
-
-
import java.util.List;
-
-
/**
-
* @author yanfengzhang
-
* @description 演示如何使用CommandLineRunner接口創建一個名為UserImportCommand的命令行工具,用于導入用戶數據到應用程序中。
-
* @date 2021/2/23 23:24
-
*/
-
-
public class UserImportCommand implements CommandLineRunner {
-
-
public void run(String... args) throws Exception {
-
List<User> users = readUserFromFile("fileName");
-
System.out.println("UserImportCommand readUserFromFile importUsers!");
-
}
-
-
// 從數據文件中讀取用戶信息
-
private List<User> readUserFromFile(String fileName) {
-
// 省略代碼,從文件中讀取用戶信息,返回一個User對象列表
-
return Lists.newArrayList();
-
}
-
}
在應用程序啟動時,CommandLineRunner接口的run方法將被自動調用,并將命令行參數作為字符串數組傳遞給run方法。
上面展示的時候并沒有使用入參,當需要導入用戶數據時,可以執行如下命令:
java -jar myapp.jar user-import users.txt
其中,myapp.jar是應用程序運行的jar包,user-import是命令行工具的名稱,users.txt是要導入的用戶數據文件名。
應用舉例二:定制化應用程序的啟動過程
可以使用CommandLineRunner接口來定制化應用程序的啟動過程。使用CommandLineRunner來執行一些自定義啟動任務:
-
package org.zyf.javabasic.springextend.runext;
-
-
import org.springframework.boot.CommandLineRunner;
-
import org.springframework.stereotype.Component;
-
-
/**
-
* @author yanfengzhang
-
* @description 演示如何使用CommandLineRunner來執行一些自定義啟動任務
-
* @date 2021/2/23 23:31
-
*/
-
-
public class StartupTasks implements CommandLineRunner {
-
-
public void run(String... args) throws Exception {
-
// 加載應用程序配置
-
System.out.println("StartupTasks configService loadConfig");
-
-
// 初始化緩存
-
System.out.println("StartupTasks cacheService initialize");
-
}
-
}
在應用程序啟動時,CommandLineRunner接口的run方法將被自動調用,并且我們的自定義啟動任務將會被執行。通過使用CommandLineRunner接口,我們可以執行任何自定義的啟動任務,以滿足特定的應用程序需求。
(五)所有擴展驗證
驗證以上配置是否生效很簡單,只需要啟動程序查看打印效果即可,以下給出一些基本的展示效果:


三、加快SpringBoot項目啟動
公司級大多數SpringBoot 項目在日常開發過程中發現服務啟動過程異常緩慢,常常需要4-7分鐘才能暴露端口,有的時候甚至在十分鐘左右,嚴重降低開發效率。

若要優化 Spring Boot 程序的啟動時間以縮短啟動時間,可以考慮:
- 減少依賴項:評估項目的依賴項,并確保只引入必要的依賴。較多的依賴項可能會增加啟動時間,因為它們需要被掃描和初始化。通過刪除不需要的依賴項或僅引入必要的模塊,可以減少類路徑的掃描和初始化時間。
- 調整自動配置:Spring Boot 的自動配置是一個強大的特性,但有時可能會引入不必要的組件和功能。通過調整自動配置,可以精確地指定所需的配置,避免加載不必要的組件,從而減少啟動時間。
- 啟用懶加載:將一些不常用的組件設置為懶加載,即在需要時才進行初始化。通過懶加載,可以避免在啟動階段初始化不必要的組件,從而加快啟動時間。可以使用 Spring Framework 的 @Lazy 注解或在配置類中進行相應的配置。
- 啟用編譯時優化:使用 Spring Boot 2.4 及更高版本,你可以通過啟用編譯時優化來加快啟動時間。通過在 pom.xml 文件中設置 <compilerArgs> 屬性,使用 --add-opens 選項來啟用編譯時優化。這可以減少反射操作的開銷,從而提高啟動性能。
- 調整日志級別:Spring Boot 默認啟用了相對較高的日志級別,這可能會導致大量的日志輸出,從而增加啟動時間。通過將日志級別調整為更低的級別,如將 INFO 調整為 WARN,可以減少日志輸出,從而縮短啟動時間。
- 使用緩存:Spring Boot 在啟動過程中會進行多個步驟的掃描和初始化。通過使用緩存機制,可以緩存一些元數據和初始化結果,避免重復的掃描和初始化操作,從而提高啟動性能。可以使用 Spring Boot 的緩存機制或其他緩存庫來實現。
(一)減少依賴項以縮短啟動時間
減少依賴項具體的分析和說明
- 評估依賴項:首先,需要仔細評估項目的依賴項。查看項目的 pom.xml(如果使用Maven)或 build.gradle(如果使用Gradle)文件,以了解所有引入的依賴項。檢查每個依賴項的用途和必要性。
- 刪除不需要的依賴項:確定哪些依賴項是不需要的或沒有被項目使用的。這些無用的依賴項可能會增加類路徑的掃描和初始化時間,從而拖慢啟動過程。可以通過移除或注釋掉不需要的依賴項來減少不必要的加載和初始化。
- 僅引入必要的模塊:對于一些大型的依賴項,例如Spring Boot的模塊,可以根據項目的需求僅引入必要的模塊。Spring Boot提供了模塊化的方式,允許你選擇性地引入只需要的模塊。通過僅引入必要的模塊,可以減少初始化過程中的掃描和加載時間。
- 排除不必要的傳遞依賴項:一些依賴項會引入其他的傳遞依賴項。如果這些傳遞依賴項不是項目所需的,可以通過在依賴項的配置中排除它們來減少類路徑的掃描和初始化。這可以通過在項目的構建文件中配置<exclusions>標簽來實現。
- 使用更輕量級的替代方案:有時,某個依賴項可能有較重的啟動開銷。在評估依賴項時,可以考慮使用更輕量級的替代方案,以減少啟動時間。例如,對于某個功能或工具庫,可能有多個不同的實現可供選擇,可以選擇具有較輕量級的實現。
通過評估和優化項目的依賴項,可以減少不必要的加載和初始化過程,從而減少啟動時間。這需要仔細分析每個依賴項的用途,并確保只引入必要的依賴項和模塊。同時,需要保持項目的功能完整性和正確性,確保刪除的依賴項不會影響項目的正常運行。
減少依賴項案例分析
當涉及到減少依賴項以縮短啟動時間的案例分析,我們以一個簡單的Web應用為例,假設我們有一個基于Spring Boot的Web應用,該應用使用了以下依賴項:
- spring-boot-starter-web:用于構建Web應用程序的基本依賴項。
- spring-boot-starter-data-jpa:用于與數據庫進行交互的依賴項。
- spring-boot-starter-security:用于添加安全性功能的依賴項。
- spring-boot-starter-test:用于測試的依賴項。
在評估這些依賴項后,我們注意到以下情況:
- 項目中并沒有使用與數據庫交互的功能,因此 spring-boot-starter-data-jpa 可能是不必要的依賴項。
- 在項目中并沒有實現任何安全性功能,因此 spring-boot-starter-security 也可能是不必要的依賴項。
基于以上分析,我們可以采取以下措施來優化啟動時間:
- 刪除不需要的依賴項:從項目的構建文件(如pom.xml)中刪除不需要的依賴項,即 spring-boot-starter-data-jpa 和 spring-boot-starter-security。
- 清理類路徑掃描和初始化:由于刪除了不必要的依賴項,應用程序在啟動時不再需要掃描和初始化與數據庫和安全性相關的組件,從而減少啟動時間。
- 進行相關測試:在進行以上更改后,確保對應用程序進行全面的測試,以確保沒有因刪除依賴項而引起的不可預料的問題。
通過這樣的優化措施,我們可以減少應用程序的啟動時間,特別是在大型項目中,當引入了大量不必要的依賴項時,效果會更為顯著。
(二)調整自動配置以縮短啟動時間
調整自動配置具體分析說明
- 了解自動配置機制:首先,了解 Spring Boot 的自動配置機制是很重要的。Spring Boot 使用條件化配置來根據項目的依賴和配置來自動決定哪些組件應該被加載和配置。條件注解(如 @ConditionalOnClass、@ConditionalOnProperty 等)用于根據特定的條件來決定組件是否應該被自動配置。
- 檢查自動配置類:查看自動配置類,了解每個自動配置類所做的配置和加載的組件。可以在 Spring Boot 的官方文檔或源代碼中找到自動配置類的詳細信息。對于不必要的組件,可以嘗試找到對應的自動配置類并進行分析。
- 排除不必要的自動配置:通過使用 @EnableAutoConfiguration 注解的 exclude 屬性,可以排除不需要的自動配置類。這可以在主應用程序類上進行配置。通過排除不必要的自動配置類,可以避免加載不需要的組件和功能,從而減少啟動時間。
- 自定義自動配置:如果發現某個自動配置類加載了不必要的組件,但又需要其他配置,可以自定義自動配置類。通過創建一個帶有 @Configuration 注解的配置類,并在其中指定所需的配置,可以覆蓋默認的自動配置。這樣可以精確地指定所需的配置,避免加載不必要的組件。
- 配置條件:有些自動配置類提供了條件注解,可以使用這些條件注解來根據項目的需求進行配置。例如,使用 @ConditionalOnProperty 注解可以根據配置屬性的值來決定是否應用該自動配置。通過合理配置這些條件,可以避免加載不必要的組件。
通過調整自動配置,可以精確地指定所需的配置,避免加載不必要的組件和功能,從而減少啟動時間。
調整自動配置案例分析
假設我們有一個基于Spring Boot的Web應用,該應用使用了以下自動配置:
- spring-boot-starter-web:用于構建Web應用程序的基本自動配置。
- spring-boot-starter-data-jpa:用于與數據庫進行交互的自動配置。
- spring-boot-starter-security:用于添加安全性功能的自動配置。
在評估這些自動配置后,我們注意到以下情況:
- 項目中并沒有使用與數據庫交互的功能,因此 spring-boot-starter-data-jpa 可能是不必要的自動配置。
- 在項目中并沒有實現任何安全性功能,因此 spring-boot-starter-security 也可能是不必要的自動配置。
基于以上分析,我們可以采取以下措施來優化啟動時間:
排除不必要的自動配置:在主應用程序類上使用 @EnableAutoConfiguration 注解的 exclude 屬性,排除 spring-boot-starter-data-jpa 和 spring-boot-starter-security 的自動配置。
-
-
public class MyApplication {
-
// ...
-
}
自定義自動配置:如果需要其他的配置,可以創建一個自定義的自動配置類,并在其中指定所需的配置。例如,對于與數據庫交互的功能,可以創建一個自定義的配置類,僅包含必要的配置項。
-
-
public class CustomDataJpaAutoConfiguration {
-
// ...
-
}
通過以上優化措施,我們排除了不必要的自動配置,并自定義了所需的配置,避免加載不必要的組件和功能,從而減少啟動時間。這種方式可以根據項目的實際需求靈活調整自動配置,以提高啟動性能。
(三)啟用懶加載以縮短啟動時間
啟用懶加載具體分析說明
啟用懶加載是一種有效的方法來減少啟動時間,只有在需要時才初始化不常用的組件。以下是具體分析說明:
- 確定需要懶加載的組件:仔細評估項目中的組件,并確定哪些組件是不常用的,可以延遲加載。這些組件可能包括大型的第三方庫、復雜的初始化過程或與特定功能相關的組件。
- 使用 @Lazy 注解:在需要懶加載的組件上使用 Spring Framework 的 @Lazy 注解。將 @Lazy 注解應用于組件的聲明處,以指示該組件應該在需要時才進行初始化。例如:
-
-
-
public class MyLazyComponent {
-
// ...
-
}
-
- 在配置類中進行配置:如果你使用的是配置類來進行組件的配置,你可以在配置類的方法上使用 @Lazy 注解,將需要懶加載的組件進行標記。例如:
-
-
public class MyConfig {
-
-
-
public MyLazyComponent myLazyComponent() {
-
return new MyLazyComponent();
-
}
-
}
-
- 重新構建和運行應用程序:保存更改后,重新構建并運行應用程序。在啟動過程中,被標記為懶加載的組件將不會立即初始化,只有在首次訪問時才會進行初始化。這樣可以避免啟動階段初始化不必要的組件,從而加快啟動時間。
通過啟用懶加載,可以延遲初始化不常用的組件,減少啟動時間。這在項目中特別適用于那些具有復雜初始化過程或與特定功能相關的組件。需要注意的是,懶加載的組件在第一次訪問時可能會引入額外的延遲,因此在評估和標記組件時要權衡性能和實際需求。
通過使用 Spring Framework 的 @Lazy 注解或在配置類中進行相應的配置,可以啟用懶加載,從而在需要時才初始化不常用的組件,加快應用程序的啟動時間。
啟用懶加載案例分析
假設我們有一個基于 Spring Boot 的電子商務應用程序,其中包含一些復雜的服務和組件。我們注意到啟動時間較長,并且發現其中一個原因是某些組件的初始化過程相對較慢。
在進一步分析后,我們確定以下組件是不常用的,并且可以通過懶加載來延遲初始化:
- PaymentService:用于處理支付的服務組件。
- SearchService:用于執行商品搜索的服務組件。
這兩個組件在啟動時不是必需的,因為它們只在用戶執行特定操作時才會被使用。
我們可以使用 Spring Framework 的 @Lazy 注解來標記這些組件,將它們設置為懶加載。
-
-
-
public class PaymentService {
-
// ...
-
}
-
-
-
-
public class SearchService {
-
// ...
-
}
通過在組件的聲明處添加 @Lazy 注解告訴 Spring 框架在需要使用這些組件時才進行初始化。
重新構建并運行應用程序后,啟動階段將不會立即初始化 PaymentService 和 SearchService 組件。只有當用戶執行相關操作時,才會觸發它們的初始化。這樣可以減少啟動時間,并且在應用程序啟動后,只有真正需要時才會占用資源。
需要注意的是,在標記組件為懶加載時,確保仔細評估其對應用程序功能的影響。確保懶加載的組件在實際使用時能夠正確地被初始化,并且不會導致功能上的問題。
(四)啟用編譯時優化以縮短啟動時間
啟用編譯時優化是一種有效的方法來加快 Spring Boot 應用程序的啟動時間,特別是在使用 Spring Boot 2.4 及更高版本時。以下是具體的分析和說明:
- 確認使用的 Spring Boot 版本:確保你的項目使用的是 Spring Boot 2.4 或更高版本。編譯時優化功能是在 Spring Boot 2.4 中引入的。
- 配置 pom.xml 文件:在項目的 pom.xml 文件中,找到 <build> 元素,并在其中的 <plugins> 元素下添加 Maven Compiler 插件配置。在 Maven Compiler 插件配置中,使用 <compilerArgs> 屬性來設置編譯器選項。
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.apache.maven.plugins</groupId>
-
<artifactId>maven-compiler-plugin</artifactId>
-
<configuration>
-
<compilerArgs>
-
<arg>--add-opens</arg>
-
<arg>java.base/java.lang=ALL-UNNAMED</arg>
-
<arg>--add-opens</arg>
-
<arg>java.base/java.util=ALL-UNNAMED</arg>
-
<!-- 添加其他需要的 --add-opens 選項 -->
-
</compilerArgs>
-
</configuration>
-
</plugin>
-
</plugins>
-
</build>
在 <compilerArgs> 屬性中,我們使用 --add-opens 選項來指定需要開放的包和模塊。上述示例中,我們設置了兩個 --add-opens 選項,分別是 java.lang 和 java.util 包。你還可以根據需要添加其他的 --add-opens 選項,以開放其他需要的包和模塊。 -
- 重新構建應用程序:保存更改后,重新構建應用程序。在編譯過程中,編譯器將使用指定的編譯器選項,啟用編譯時優化。
啟用編譯時優化可以減少反射操作的開銷,從而提高應用程序的啟動性能。它通過使用 --add-opens 選項來開放特定的包和模塊,使得一些反射操作可以直接訪問,而無需通過反射 API。
需要注意的是,編譯時優化可能會因為項目的具體情況而有所不同。在應用該優化時,需要進行測試和評估,確保應用程序在啟用編譯時優化后仍然能夠正常運行。
通過在 pom.xml 文件中配置 Maven Compiler 插件,使用 <compilerArgs> 屬性設置 --add-opens 選項,可以啟用編譯時優化來加快 Spring Boot 應用程序的啟動時間。根據項目的需求,你可以添加其他需要的 --add-opens 選項,以滿足特定的優化需求。
(五)調整日志級別以縮短啟動時間
調整日志級別是一種簡單而有效的方法來減少啟動時間,特別是當應用程序的日志輸出量較大時。以下是具體的分析和說明:
- 確定適當的日志級別:仔細評估你的應用程序,并確定適當的日志級別。通常,將日志級別從較高的級別(如 DEBUG 或 INFO)調整為較低的級別(如 WARN 或 ERROR)是一種常見的優化策略。較低的日志級別會減少輸出的日志消息數量。
- 配置日志級別:在 Spring Boot 應用程序的配置文件(如 application.properties 或 application.yml)中,找到與日志相關的配置項。根據你使用的日志框架(如 Logback、Log4j2 或 JUL),進行相應的配置。例如,對于 Logback,你可以在 application.properties 文件中添加以下配置:
或者在 application.yml 文件中添加以下配置:logging.level.root=WARN-
logging:
-
level:
-
root: WARN
這將將根日志記錄器的級別設置為 WARN,從而只輸出 WARN 級別及以上的日志消息。 -
- 重新構建和運行應用程序:保存更改后,重新構建并運行應用程序。在啟動過程中,日志框架將根據配置的日志級別來決定是否輸出特定級別的日志消息。通過將日志級別調整為較低的級別,可以減少輸出的日志消息數量,從而加快啟動時間。
通過將日志級別調整為較低的級別,如將 INFO 調整為 WARN,可以減少日志輸出量,從而縮短應用程序的啟動時間。在調整日志級別時,需要確保仍能獲取到足夠的日志信息以滿足應用程序的需求。
(六)使用緩存以縮短啟動時間
使用緩存是一種有效的方法來提高 Spring Boot 應用程序的啟動性能。通過緩存一些元數據和初始化結果,可以避免重復的掃描和初始化操作,從而減少啟動時間。以下是具體的分析和說明:
- 使用 Spring Boot 的緩存機制:Spring Boot 提供了內置的緩存機制,可以通過使用 @EnableCaching 注解來啟用緩存功能。在需要緩存的組件或方法上添加 @Cacheable 注解,以指示該組件或方法的結果應該被緩存。
-
-
-
public class MyApplication {
-
// ...
-
}
-
-
-
public class MyService {
-
-
public MyData getData(String key) {
-
// 從數據庫或其他地方獲取數據
-
// ...
-
}
-
}
在上述示例中,我們通過在 MyService 的 getData() 方法上添加 @Cacheable("myCache") 注解,指定該方法的結果應該被緩存,并使用名為 "myCache" 的緩存區域。通過緩存結果,當多次調用 getData() 方法時,如果相同的參數值被傳遞進來,將會直接從緩存中獲取結果,而不需要重復的數據獲取操作。 -
- 使用其他緩存庫:除了使用 Spring Boot 的緩存機制外,你還可以選擇使用其他的緩存庫,如 Ehcache、Redis 或 Caffeine。這些緩存庫提供了更高級的緩存功能和配置選項,可以滿足更復雜的需求。例如,使用 Ehcache 作為緩存庫,你可以在應用程序的配置文件中配置 Ehcache 緩存:
-
<dependency>
-
<groupId>org.ehcache</groupId>
-
<artifactId>ehcache</artifactId>
-
</dependency>
然后,在需要緩存的方法上使用 Ehcache 注解,如 @org.ehcache.Cacheable。使用其他緩存庫時,需要根據庫的文檔和配置方式來進行相應的設置和使用。 -
無論是使用 Spring Boot 的緩存機制還是其他緩存庫,通過緩存一些元數據和初始化結果,可以避免重復的掃描和初始化操作,從而減少啟動時間。但需要注意的是,在使用緩存時,要確保緩存的數據和結果的一致性,并考慮緩存的過期策略和清理機制,以避免緩存數據過時或占用過多內存。
使用緩存是一種優化啟動時間的有效方法。可以使用 Spring Boot 的緩存機制或其他緩存庫來實現緩存功能,并根據具體需求和庫的使用方式進行配置和使用。通過緩存一些元數據和初始化結果,可以避免重復操作,提高應用程序的啟動性能。
四、總結
SpringBoot 作為現代應用開發的主流框架,提供了豐富的功能和靈活的擴展性,但在面對大規模系統時,其啟動時間成為了不可忽視的問題。通過本文的探討,我們了解到,優化 SpringBoot 啟動時間不僅僅是一個簡單的性能提升問題,更涉及到架構設計、資源管理以及框架內部機制的深度理解。
在本文中,我們提出了兩大優化策略:干預和優化。干預策略通過分析并優化應用啟動過程中的瓶頸,能夠有效減少不必要的初始化和加載操作。而優化策略則從源頭入手,通過合理配置和調整應用的啟動順序、資源管理及依賴關系,進一步提升啟動速度。這些方法不僅能夠加速 SpringBoot 應用的啟動時間,還能提高整個系統的響應效率,帶來更好的用戶體驗。
盡管 SpringBoot 的啟動優化是一項系統性的工程,但通過有效的策略和工具,我們可以逐步解決這一問題。優化的過程并非一蹴而就,需要開發者不斷實驗和調整,尋找適合自己項目的最佳方案。希望本文的總結與指導,能為你在優化 SpringBoot 啟動時間的道路上提供啟發和幫助,進而提升應用的性能和穩定性。
未來,隨著 SpringBoot 和相關技術的不斷演進,我們也可以期待更多高效的啟動優化工具和技術的出現,幫助開發者輕松應對啟動時間挑戰,推動微服務架構的持續發展。

浙公網安備 33010602011771號