請一定記??!Spring Boot 執(zhí)行初始化操作的七種王炸手段
環(huán)境:SpringBoot3.4.2
1. 簡介
在 Spring Boot 應(yīng)用開發(fā)中,初始化操作是非常關(guān)鍵的操作。它可以在應(yīng)用啟動時進行一系列預先設(shè)定的任務(wù),例如加載配置文件、初始化數(shù)據(jù)庫相關(guān)操作、預熱緩存數(shù)據(jù)、注冊全局事件監(jiān)聽器等。
合理的初始化能讓應(yīng)用啟動即穩(wěn)定高效。但很多開發(fā)者對 Spring Boot 提供的豐富初始化手段了解不深。別擔心,本篇文章將為你詳細介紹 Spring Boot 提供的 7 種初始化操作,通過代碼示例,助你輕松掌握,靈活運用。
環(huán)境準備:準備下面的類,后續(xù)介紹的示例代碼都會基于下面的類進行。
@Service
public class CommonService {
}
@Component
public class PackComponent {
@Resource
private CommonService commonService ;
}
2. 實戰(zhàn)案例
2.1 @PostConstruct注解
該注解應(yīng)用在Bean的方法上,在Bean的依賴注入完成以后執(zhí)行。
// 構(gòu)造函數(shù)中打印注入的CommonService
public PackComponent() {
System.err.printf("執(zhí)行構(gòu)造函數(shù), commonService: %s%n", this.commonService) ;
}
// 添加初始化方法
@PostConstruct
public void init() {
System.err.printf("@PostConstruct 執(zhí)行初始化方法init, commonService: %s%n", this.commonService) ;
}
啟動容器,輸出結(jié)果:
執(zhí)行構(gòu)造函數(shù), commonService: null
@PostConstruct 執(zhí)行初始化方法init, commonService: CommonService@7cfb4736
適用場景:單個Bean的簡單初始化(如緩存預熱、資源加載等)。
注意:
@PostConstruct 注解類型在 JDK 6 到 JDK 8 期間是標準 Java 庫的一部分。然而,在 JDK 9 中,整個 javax.annotation 包從核心 Java 模塊中分離了出來,并最終在 JDK 11 中被移除。自 Jakarta EE 9 起,該包現(xiàn)在位于 jakarta.annotation 之下。
2.2 InitializingBean接口
只需將我們的Bean實現(xiàn)該接口并重寫afterPropertiesSet()方法。
@Component
public class PackComponent implements InitializingBean {
// ...
@Override
public void afterPropertiesSet() throws Exception {
System.err.printf("InitializingBean初始化bean, "
+ "commonService: %s%n", this.commonService) ;
}
}
啟動容器,輸出結(jié)果:
執(zhí)行構(gòu)造函數(shù), commonService: null
InitializingBean初始化bean, commonService: CommonService@3811510
適用場景:說不出有什么特別的場景??。不過來看看官方的建議是怎么說的:
建議您不要使用 InitializingBean 接口,因為它會將代碼不必要地耦合到 Spring上。相反,我們建議使用 @PostConstruct 注解,或者指定一個 POJO 的初始化方法。
注意:該初始化動作在@PostConstruct之后執(zhí)行。
2.3 @Bean的initMethod屬性
通過@Bean注解定義bean時,我們可以通過設(shè)置initMethod屬性來指定執(zhí)行的初始化方法。
現(xiàn)有如下的類:
public class ThirdPartyComponent {
public void init() {
System.err.println("ThirdPartyComponent init...") ;
}
}
配置該類為Bean對象:
@Bean(initMethod = "init")
ThirdPartyComponent thirdPartyComponent() {
return new ThirdPartyComponent() ;
}
啟動容器,輸出結(jié)果:
ThirdPartyComponent init...
適用場景:第三方類(無法直接添加注解)的初始化。
注意:該初始化操作的執(zhí)行是在@PostConstruct和InitializingBean之后執(zhí)行。
2.4 ApplicationRunner或CommandLineRunner
將我們的bean實現(xiàn)其中任何一個接口都可以,在Spring Boot應(yīng)用啟動完成后執(zhí)行。
執(zhí)行時機:會在ApplicationContext上下完全準備就緒以后(refresh之后)。
@Component
public class PackComponent implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.err.println("通過CommandLineRunner執(zhí)行初始化動作...") ;
}
}
啟動容器,輸出結(jié)果:
圖片
應(yīng)用啟動完成以后執(zhí)行相應(yīng)的Runner接口回調(diào)方法。
ApplicationRunner與CommandLineRunner的區(qū)別在于接收命令行參數(shù)方式不同。ApplicationRunner的回調(diào)方法接收ApplicationArguments參數(shù),該對象支持解析命令行參數(shù)(如--key=value);CommandLineRunner則是直接接收原始命令行參數(shù)(String[])。
適用場景:應(yīng)用啟動后執(zhí)行全局任務(wù)(如數(shù)據(jù)初始化,資源檢查等)。
2.5 監(jiān)聽ContextRefreshedEvent事件
該事件會在ApplicationContext初始化或刷新完成后觸發(fā)(refresh單例bean創(chuàng)建完成以后的最后一步finishRefresh方法),如下:
圖片
@Component
public class PackComponent implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.err.println("ApplicationContext初始化完成, 執(zhí)行初始化操作") ;
}
}
啟動容器,輸出結(jié)果:
圖片
適用場景:適合應(yīng)用到需Spring上下文初始化完成后執(zhí)行全局性初始化操作的場景(如:字典數(shù)據(jù),緩存,連接外部系統(tǒng)等)。
2.6 SmartInitializingSingleton接口
實現(xiàn)該接口后在所有的單例bean都創(chuàng)建完成以后執(zhí)行該接口的回調(diào)方法afterSingletonsInstantiated()。
執(zhí)行時機:在@PostConstruct、InitializingBean之后,但在ApplicationRunner之前。
@Component
public class PackComponent implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.err.println("SmartInitializingSingleton執(zhí)行初始化操作") ;
}
}
啟動容器,輸出結(jié)果:
圖片
使用場景:依賴其他單例 Bean 的而全景初始化(如緩存預熱,與上下文無關(guān)的初始化操作等)。
2.7 SmartLifecycle接口
SmartLifecycle 用于組件的生命周期管理。實現(xiàn)該接口可定義自定義初始化(start)和銷毀(stop)邏輯。
執(zhí)行時機:該接口的調(diào)用與2.5介紹的時機一樣finishRefresh中,但是比ContextRefreshedEvent事件先執(zhí)行。
@Component
public class PackComponent implements SmartLifecycle {
private volatile boolean running;
@Override
public void start() {
System.err.println("SmartLifecycle 執(zhí)行初始化操作") ;
this.running = true ;
}
@Override
public void stop() {
this.running = false;
}
@Override
public boolean isRunning() {
return this.running ;
}
}
啟動容器,輸出結(jié)果:
圖片
適用場景:外部資源管理,緩存預熱,異步消息處理等。