SpringBoot啟動(dòng)時(shí)執(zhí)行初始化操作的幾種方式
場(chǎng)景
項(xiàng)目中,經(jīng)常需要在啟動(dòng)過(guò)程中初始化一些數(shù)據(jù),如從數(shù)據(jù)庫(kù)讀取一些配置初始化,或從數(shù)據(jù)庫(kù)讀取一些熱點(diǎn)數(shù)據(jù)到redis進(jìn)行初始化緩存。
方式一:實(shí)現(xiàn)CommandLineRunner 接口重寫run方法邏輯
CommandLineRunner是Spring提供的接口,定義了一個(gè)run()方法,用于執(zhí)行初始化操作。
import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class InitConfigCommand implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner:"+"{}"+"接口實(shí)現(xiàn)方式重寫"); } }
CommandLineRunner的執(zhí)行時(shí)機(jī)為Spring beans初始化之后,因此CommandLineRunner的執(zhí)行一定是晚于@PostConstruct的。
若有多組初始化操作,則每一組操作都要定義一個(gè)CommandLineRunner派生類并實(shí)現(xiàn)run()方法。這些操作的執(zhí)行順序使用@Order(n)來(lái)設(shè)置,n為int型數(shù)據(jù)。
@Component @Order(99) public class CommandLineRunnerA implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("初始化:CommandLineRunnerA"); } } @Component @Order(1) public class CommandLineRunnerB implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("初始化:CommandLineRunnerB"); } }
如上,會(huì)先執(zhí)行CommandLineRunnerB的run(),再執(zhí)行CommandLineRunnerA的run()。
@Order(n)中的n較小的會(huì)先執(zhí)行,較大的后執(zhí)行。n只要是int值即可,無(wú)需順序遞增。
方式二:實(shí)現(xiàn)ApplicationRunner接口重寫run方法邏輯
ApplicationRunner接口與CommandLineRunner接口類似,都需要實(shí)現(xiàn)run()方法。二者的區(qū)別在于run()方法的參數(shù)不同:
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class InitConfig implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("項(xiàng)目啟動(dòng)初始化"); } }
ApplicationRunner接口的run()參數(shù)為ApplicationArguments對(duì)象,因此可以獲取更多項(xiàng)目相關(guān)的內(nèi)容。
ApplicationRunner接口與CommandLineRunner接口的調(diào)用時(shí)機(jī)也是相同的,都是Spring beans初始化之后。因此ApplicationRunner接口也使用@Order(n)來(lái)設(shè)置執(zhí)行順序。
方式三:使用@PostConstruct注解的方式
對(duì)于注入到Spring容器中的類,在其成員函數(shù)前添加@PostConstruct注解,則在執(zhí)行Spring beans初始化時(shí),就會(huì)執(zhí)行該函數(shù)。
但由于該函數(shù)執(zhí)行時(shí),其他Spring beans可能并未初始化完成,因此在該函數(shù)中執(zhí)行的初始化操作應(yīng)當(dāng)不依賴于其他Spring beans。
@Component public class Construct { @PostConstruct public void doConstruct() throws Exception { System.out.println("初始化:PostConstruct"); } }
初始化順序
- @PostConstruct 注解方法
- CommandLineRunner接口實(shí)現(xiàn)
- ApplicationRunner接口實(shí)現(xiàn)
擴(kuò)展
@PostConstruct注解使用在方法上,它可以被用來(lái)標(biāo)注一個(gè)非靜態(tài)的 void 方法,這個(gè)方法會(huì)在該類被 Spring 容器初始化后立即執(zhí)行。因?yàn)樗膱?zhí)行時(shí)機(jī)是在依賴注入之后,對(duì)象構(gòu)造完成之后,也就是說(shuō)是在@Autowired注入之后執(zhí)行。所以這里可以進(jìn)行一些初始化操作,如某些需要在對(duì)象創(chuàng)建后才能進(jìn)行的數(shù)據(jù)初始化操作。
需要注意以下幾點(diǎn):
- @PostConstruct 只能用在方法上面,而不能用在屬性或構(gòu)造函數(shù)上。
- 一個(gè)類中可以有多個(gè)使用 @PostConstruct 注解的方法,但執(zhí)行順序并不是固定的。
- @PostConstruct 注解的方法在本類中必須是無(wú)參數(shù)的,如果有參數(shù),那么這個(gè)方法不會(huì)被執(zhí)行。
- @PostConstruct 注解的方法在實(shí)現(xiàn)上可以使用任意修飾符。
假設(shè)我們有一個(gè)需要初始化數(shù)據(jù)的類:
public class InitService { private List<String> data; public InitService() { this.data = Arrays.asList("A", "B", "C"); } @PostConstruct public void init() { data.add("D"); } public List<String> getData() { return this.data; } }
當(dāng)我們實(shí)例化 InitService 時(shí),構(gòu)造函數(shù)會(huì)為 data 屬性賦初值,而 @PostConstruct 注解的 init 方法會(huì)在 Spring 容器實(shí)例化完 InitService 后被執(zhí)行,將 “D” 添加到 data 列表中。所以當(dāng)我們調(diào)用 getData() 方法時(shí),返回的列表應(yīng)該是 [A, B, C, D]。
接下來(lái)看看 @Autowired 和@PostConstruct 的具體執(zhí)行順序
@Service public class TestA { static { System.out.println("staticA"); } @Autowired private TestB testB; public TestA() { System.out.println("這是TestA 的構(gòu)造方法"); } @PostConstruct private void init() { System.out.println("這是TestA的 init 方法"); testB.test(); } }
@Service public class TestB { static { System.out.println("staticB"); } @PostConstruct private void init() { System.out.println("這是TestB的init 方法"); } public TestB() { System.out.println("這是TestB的構(gòu)造方法"); } void test() { System.out.println("這是TestB的test方法"); } }
構(gòu)造方法:在對(duì)象初始化時(shí)執(zhí)行。執(zhí)行順序在static靜態(tài)代碼塊之后。
服務(wù)啟動(dòng)后,輸出結(jié)果如下:
staticA
這是TestA 的構(gòu)造方法
staticB
這是TestB的構(gòu)造方法
這是TestB的init 方法
這是TestA的 init 方法
這是TestB的test方法
結(jié)論為:等@Autowired注入后,在執(zhí)行@PostConstruct注解的方法。
方式四:靜態(tài)代碼塊
static靜態(tài)代碼塊,在類加載的時(shí)候即自動(dòng)執(zhí)行。
使用的static靜態(tài)代碼塊,實(shí)現(xiàn)原理為@Component + static代碼塊, spring boot項(xiàng)目在啟動(dòng)過(guò)程中,會(huì)掃描@Component 并初始化相應(yīng)的類,類的初始化過(guò)程會(huì)運(yùn)行靜態(tài)代碼塊。
@Component public class WordInitConfig { static { WordSegmenter.segWithStopWords("初始化分詞"); } }
所以得到結(jié)論
static>constructer>@Autowired>@PostConstruct>ApplicationRunner>CommandLineRunner
到此這篇關(guān)于SpringBoot啟動(dòng)時(shí)執(zhí)行初始化操作的幾種方式的文章就介紹到這了,更多相關(guān)SpringBoot執(zhí)行初始化操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
避免sql注入_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了避免sql注入,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08Spring中的事件監(jiān)聽(tīng)器使用學(xué)習(xí)記錄
Spring框架中的事件監(jiān)聽(tīng)機(jī)制是一種設(shè)計(jì)模式,它允許你定義和觸發(fā)事件,同時(shí)允許其他組件監(jiān)聽(tīng)這些事件并在事件發(fā)生時(shí)作出響應(yīng),這篇文章主要介紹了Spring中的事件監(jiān)聽(tīng)器使用學(xué)習(xí),需要的朋友可以參考下2024-07-07解決程序啟動(dòng)報(bào)錯(cuò)org.springframework.context.ApplicationContextExcept
文章描述了一個(gè)Spring Boot項(xiàng)目在不同環(huán)境下啟動(dòng)時(shí)出現(xiàn)差異的問(wèn)題,通過(guò)分析報(bào)錯(cuò)信息,發(fā)現(xiàn)是由于導(dǎo)入`spring-boot-starter-tomcat`依賴時(shí)定義的scope導(dǎo)致的配置問(wèn)題,調(diào)整依賴導(dǎo)入配置后,解決了啟動(dòng)錯(cuò)誤2024-11-11IDEA 中 maven 的 Lifecycle 和Plugins&n
IDEA 主界面右側(cè) Maven 標(biāo)簽欄有同樣的命令,比如 install,既在 Plugins 中存在,也在 Lifecycle中存在,到底選哪個(gè)?二者又有什么區(qū)別呢?下面小編給大家介紹下IDEA 中 maven 的 Lifecycle 和Plugins 的區(qū)別,感興趣的朋友一起看看吧2023-03-03SpringBoot如何使用ApplicationContext獲取bean對(duì)象
這篇文章主要介紹了SpringBoot 如何使用ApplicationContext獲取bean對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Spring Boot教程之利用ActiveMQ實(shí)現(xiàn)延遲消息
這篇文章主要給大家介紹了關(guān)于Spring Boot教程之利用ActiveMQ實(shí)現(xiàn)延遲消息的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11