ApplicationRunner、InitializingBean、@PostConstruct執(zhí)行順序解讀
概述
開發(fā)中可能會(huì)有這樣的場景,需要在容器啟動(dòng)的時(shí)候執(zhí)行一些內(nèi)容。比如讀取配置文件,數(shù)據(jù)庫連接之類的。
SpringBoot給我們提供了兩個(gè)接口來幫助我們實(shí)現(xiàn)這種需求。
兩個(gè)啟動(dòng)加載接口分別是:CommandLineRunner和ApplicationRunner。
Spring 提供了接口 InitializingBean,jdk提供了@PostConstruct
CommandLineRunner和ApplicationRunner區(qū)別
CommandLineRunner和ApplicationRunner的作用是相同的。不同之處在于CommandLineRunner接口的run()方法接收String數(shù)組作為參數(shù),即是最原始的參數(shù),沒有做任何處理;而ApplicationRunner接口的run()方法接收ApplicationArguments對象作為參數(shù),是對原始參數(shù)做了進(jìn)一步的封裝。
當(dāng)程序啟動(dòng)時(shí),我們傳給main()方法的參數(shù)可以被實(shí)現(xiàn)CommandLineRunner和ApplicationRunner接口的類的run()方法訪問,即可接收啟動(dòng)服務(wù)時(shí)傳過來的參數(shù)。我們可以創(chuàng)建多個(gè)實(shí)現(xiàn)CommandLineRunner和ApplicationRunner接口的類。為了使他們按一定順序執(zhí)行,可以使用@Order注解或?qū)崿F(xiàn)Ordered接口。
ApplicationRunner接口的示例
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
@Order(value = 1)
public class JDDRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("這個(gè)是測試ApplicationRunner接口");
String strArgs = Arrays.stream(arg0.getSourceArgs()).collect(Collectors.joining("|"));
System.out.println("Application started with arguments:" + strArgs);
}
}啟動(dòng)時(shí)候指定參數(shù):java -jar xxxx.jar data1 data2 data3
這個(gè)是測試ApplicationRunner接口
Application started with arguments:data1|data2|data3
CommandLineRunner接口示例
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("這個(gè)是測試CommandLineRunn接口");
String strArgs = Arrays.stream(args).collect(Collectors.joining("|"));
System.out.println("Application started with arguments:" + strArgs);
}
}啟動(dòng)時(shí)候指定參數(shù):java -jar xxxx.jar data1 data2 data3
運(yùn)行結(jié)果:
這個(gè)是測試CommandLineRunn接口
Application started with arguments:data1|data2|data3
CommandLineRunner和ApplicationRunner的執(zhí)行順序
在spring boot程序中,我們可以使用不止一個(gè)實(shí)現(xiàn)CommandLineRunner和ApplicationRunner的bean。
為了有序執(zhí)行這些bean的run()方法,可以使用@Order注解或Ordered接口。
@Component
@Order(2)
public class ApplicationRunnerBean1 implements ApplicationRunner {
@Override
public void run(ApplicationArguments arg0) throws Exception {
System.out.println("ApplicationRunnerBean 1");
}
}
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {
@Override
public void run(ApplicationArguments arg0) throws Exception {
System.out.println("ApplicationRunnerBean 2");
}
}
@Component
@Order(1)
public class CommandLineRunnerBean1 implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("CommandLineRunnerBean 1");
}
}
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("CommandLineRunnerBean 2");
}
}執(zhí)行結(jié)果:
CommandLineRunnerBean 1 ApplicationRunnerBean 1 CommandLineRunnerBean 2 ApplicationRunnerBean 2
實(shí)現(xiàn)多個(gè)ApplicationRunner時(shí)部分接口未執(zhí)行
@Component
@Slf4j
public class RunnerTest1 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
while (true) {
System.out.println("this is RunnerTest1");
Thread.sleep(100);
}
}
}
@Component
@Slf4j
public class RunnerTest2 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
while (true) {
System.out.println("this is RunnerTest2");
Thread.sleep(100);
}
}
}只會(huì)執(zhí)行RunnerTest1中方法。
通過分析springboot啟動(dòng)的源碼可以發(fā)現(xiàn),在applicationContext容器加載完成之后,會(huì)調(diào)用SpringApplication類的callRunners方法:

該方法中會(huì)獲取所有實(shí)現(xiàn)了ApplicationRunner和CommandLineRunner的接口bean,然后依次執(zhí)行對應(yīng)的run方法,并且是在同一個(gè)線程中執(zhí)行。因此如果有某個(gè)實(shí)現(xiàn)了ApplicationRunner接口的bean的run方法一直循環(huán)不返回的話,后續(xù)的代碼將不會(huì)被執(zhí)行。
InitializingBean接口的用法
InitializingBean接口為bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是繼承該接口的類,在初始化bean的時(shí)候都會(huì)執(zhí)行該方法。
注意,實(shí)現(xiàn)該接口的最好加上Spring的注解注入,比如@Component
@PostConstruct注解的用法
如果想在生成對象時(shí)候完成某些初始化操作,而偏偏這些初始化操作又依賴于依賴注入,那么就無法在構(gòu)造函數(shù)中實(shí)現(xiàn)。為此,可以使用@PostConstruct注解一個(gè)方法來完成初始化,@PostConstruct注解的方法將會(huì)在依賴注入完成后被自動(dòng)調(diào)用。
優(yōu)先級: Constructor >> @Autowired >> @PostConstruct
@Component
public class Test implements InitializingBean, ApplicationRunner, CommandLineRunner {
@PostConstruct
public void init(){
System.out.println("PostConstruct 方法執(zhí)行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean 方法執(zhí)行");
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("這個(gè)是測試ApplicationRunner接口");
}
@Override
public void run(String... args) throws Exception {
System.out.println("這個(gè)是測試CommandLineRunn接口");
}
}PostConstruct 方法執(zhí)行 InitializingBean 方法執(zhí)行
這個(gè)是測試ApplicationRunner接口 這個(gè)是測試CommandLineRunn接口
由此可知: @PostConstruct>InitializingBean>ApplicationRunner>CommandLineRunner
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- springboot中ApplicationRunner執(zhí)行順序問題小結(jié)
- SpringBoot使用CommandLineRunner和ApplicationRunner執(zhí)行初始化業(yè)務(wù)方式
- Spring中的ApplicationRunner接口的使用詳解
- Spring接口ApplicationRunner用法詳解
- SpringBoot之ApplicationRunner解析(spring容器啟動(dòng)完成執(zhí)行的類)
- SchedulingConfigurer實(shí)現(xiàn)動(dòng)態(tài)定時(shí),導(dǎo)致ApplicationRunner無效解決
- SpringBoot實(shí)現(xiàn)多個(gè)ApplicationRunner時(shí)部分接口未執(zhí)行問題
相關(guān)文章
Spring?Boot中MongoTemplate從入門到實(shí)戰(zhàn)深度解析
本文介紹了Spring?Data MongoDB中的MongoTemplate,包括主要特性、核心配置、基礎(chǔ)CRUD、高級查詢與聚合操作、GridFS操作、性能優(yōu)化與監(jiān)控以及最佳實(shí)踐,通過全面的示例項(xiàng)目,展示了如何在Spring?Boot應(yīng)用中使用MongoTemplate進(jìn)行數(shù)據(jù)庫操作,感興趣的朋友跟隨小編一起看看吧2026-01-01
java map轉(zhuǎn)Multipart/form-data類型body實(shí)例
這篇文章主要介紹了java map轉(zhuǎn)Multipart/form-data類型body實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05
Java8 新特性Lambda表達(dá)式實(shí)例詳解
這篇文章主要介紹了Java8 新特性Lambda表達(dá)式實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03
SpringBoot @ComponentScan掃描的局限性方式
文章總結(jié):SpringBoot的@ComponentScan注解在掃描組件時(shí)存在局限性,只能掃描指定的包及其子包,無法掃描@SpringBootApplication注解自動(dòng)配置的組件,使用@SpringBootApplication注解可以解決這一問題,它集成了@Configuration、@EnableAutoConfiguration2025-01-01
基于mybatis 動(dòng)態(tài)SQL查詢總結(jié)
這篇文章主要介紹了mybatis 動(dòng)態(tài)SQL查詢總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java讀取excel文件并復(fù)制(copy)文件到指定目錄示例
這篇文章主要介紹了java讀取excel文件并復(fù)制文件到指定目錄示例,需要的朋友可以參考下2014-02-02
SpringBoot啟動(dòng)時(shí)如何修改上下文
本文介紹了如何在Spring Boot啟動(dòng)時(shí)修改上下文,以便加載封裝JAR中的國際化文件,通過在resources目錄下的META-INF文件夾中的spring.factories文件中配置指定類,可以實(shí)現(xiàn)這一功能2024-11-11

