SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽超詳細(xì)講解
SpringBoot在2.4版本以后默認(rèn)不加載bootstrap.yml配置項(xiàng)。
如果需要加載該配置項(xiàng),需要引入依賴,通常Spring Cloud工程配合nacos這種配置中心或注冊中心使用時(shí),需要引入該依賴。
SpringBoot單體工程無需引入該依賴,所有配置放在application.yml中即可。
<!-- bootstrap 啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
配置項(xiàng)
SpringBoot工程在啟動(dòng)時(shí),會(huì)通過SpringFactoriesLoader檢索META-INF/spring.factories文件,并加載其中的配置項(xiàng)。
常見的配置項(xiàng)有如下幾種:
- ApplicationContextInitializer:為在ApplicationContext執(zhí)行refresh之前,調(diào)用ApplicationContextInitializer的initialize()方法,對(duì)ApplicationContext做進(jìn)一步的設(shè)置和處理
- SpringApplicationRunListener:SpringBoot只提供一個(gè)實(shí)現(xiàn)類EventPublishingRunListener,在SpringBoot啟動(dòng)過程中,負(fù)責(zé)注冊ApplicationListener監(jiān)聽器,在不同的時(shí)點(diǎn)發(fā)布不同的事件類型,如果有哪些ApplicationListener的實(shí)現(xiàn)類監(jiān)聽了這些事件,則可以接收并處理
- ApplicationListener:事件監(jiān)聽器,其作用可以理解為在SpringApplicationRunListener發(fā)布通知事件時(shí),由ApplicationListener負(fù)責(zé)接收
啟動(dòng)順序說明
構(gòu)造函數(shù):初始化web容器,加載ApplicationContextInitializer的實(shí)現(xiàn)類并將其實(shí)例化,加載ApplicationListener的實(shí)現(xiàn)類并將其實(shí)例化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 初始化web容器類型,默認(rèn)SERVLET,如果存在org.springframework.web.reactive.DispatcherHandler,則是REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//找到*META-INF/spring.factories*中聲明的所有ApplicationContextInitializer的實(shí)現(xiàn)類并將其實(shí)例化
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//找到*META-INF/spring.factories*中聲明的所有ApplicationListener的實(shí)現(xiàn)類并將其實(shí)例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//獲得當(dāng)前執(zhí)行main方法的類對(duì)象
this.mainApplicationClass = deduceMainApplicationClass();
}
啟動(dòng)方法
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 創(chuàng)建bootstrap上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
//通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,
//找到聲明的所有SpringApplicationRunListener的實(shí)現(xiàn)類并將其實(shí)例化,
//之后逐個(gè)調(diào)用其started()方法,廣播SpringBoot要開始執(zhí)行了。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍歷調(diào)用所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準(zhǔn)備完畢。
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//決定是否打印Banner
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//根據(jù)webApplicationType的值來決定創(chuàng)建何種類型的ApplicationContext對(duì)象
//如果是SERVLET環(huán)境,則創(chuàng)建AnnotationConfigServletWebServerApplicationContext
//如果是REACTIVE環(huán)境,則創(chuàng)建AnnotationConfigReactiveWebServerApplicationContext
//否則創(chuàng)建AnnotationConfigApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//為ApplicationContext加載environment,之后逐個(gè)執(zhí)行ApplicationContextInitializer的initialize()方法來進(jìn)一步封裝ApplicationContext,
//并調(diào)用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個(gè)空的contextPrepared()方法】,
//之后初始化IoC容器,并調(diào)用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成,
//這里就包括通過**@EnableAutoConfiguration**導(dǎo)入的各種自動(dòng)配置類。
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//初始化所有自動(dòng)配置類,調(diào)用ApplicationContext的refresh()方法
refreshContext(context);
//目前該方法為空
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//調(diào)用所有的SpringApplicationRunListener的started()方法,廣播SpringBoot已經(jīng)完成了ApplicationContext初始化的全部過程。
listeners.started(context, timeTakenToStartup);
//遍歷所有注冊的ApplicationRunner和CommandLineRunner,并執(zhí)行其run()方法。
//該過程可以理解為是SpringBoot完成ApplicationContext初始化前的最后一步工作,
//我們可以實(shí)現(xiàn)自己的ApplicationRunner或者CommandLineRunner,來對(duì)SpringBoot的啟動(dòng)過程進(jìn)行擴(kuò)展。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}擴(kuò)展SpringApplication
我們的程序經(jīng)常需要在啟動(dòng)過程中或啟動(dòng)完成后做一些額外的邏輯處理,那么可以通過以下三種方式處理:
1、創(chuàng)建ApplicationContextInitializer的實(shí)現(xiàn)類
1)創(chuàng)建實(shí)現(xiàn)類
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
logger.info("MyApplicationContextInitializer, {}", applicationContext.getApplicationName());
}
}
2)配置META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\ com.hsoft.demo.MyApplicationContextInitializer
3)或者修改啟動(dòng)方法,調(diào)用addInitializers添加
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers(new MyApplicationContextInitializer());
springApplication.run(args);
}
2、創(chuàng)建ApplicationListener的實(shí)現(xiàn)類
ApplicationListener也有兩種方式,首先創(chuàng)建實(shí)現(xiàn)類,然后修改啟動(dòng)方法,調(diào)用addListeners添加,或者直接添加注解@Component
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationReadyEvent> {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
logger.info("MyApplicationListener,{}",event.toString());
}
}
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addListeners(new MyApplicationListener());
springApplication.run(args);
}
也可以通過配置META-INF/spring.factories實(shí)現(xiàn)
# Application Listeners
org.springframework.context.ApplicationListener=\
com.hsoft.demo.MyApplicationListener
推薦直接使用注解@Component或addListeners()方式,如果配置META-INF/spring.factories,因bootstrap配置分開加載所以監(jiān)聽程序會(huì)被觸發(fā)兩次
3、創(chuàng)建ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類
只需創(chuàng)建一個(gè)實(shí)現(xiàn)類型,并在實(shí)現(xiàn)類上面增加注解@Component即可
@Component
public class MyApplicationRunner implements ApplicationRunner {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("MyApplicationRunner, {}",args.getOptionNames());
}
}
@Component
public class MyCommandLineRunner implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void run(String... args) throws Exception {
logger.info("MyCommandLineRunner, {}", args);
}
}生成war包在web容器(tomcat)中部署
如果SpringBoot工程要在Tomcat中部署,需要通過如下操作:
1、修改成war工程
2、嵌入式Tomcat依賴scope指定provided
3、編寫SpringBootServletInitializer類子類,并重寫configure方法
/**
* web容器中進(jìn)行部署
*
*/
public class MyServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
}
到此這篇關(guān)于SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽超詳細(xì)講解的文章就介紹到這了,更多相關(guān)SpringBoot工程啟動(dòng)順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis中點(diǎn)擊mapper接口快速定位到對(duì)應(yīng)xml中sql方式
這篇文章主要介紹了mybatis中點(diǎn)擊mapper接口快速定位到對(duì)應(yīng)xml中sql方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
詳解SpringBoot開發(fā)案例之整合Dubbo分布式服務(wù)
這篇文章主要介紹了詳解SpringBoot開發(fā)案例之整合Dubbo分布式服務(wù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
MyBatis使用Zookeeper保存數(shù)據(jù)庫的配置可動(dòng)態(tài)刷新的實(shí)現(xiàn)代碼
這篇文章主要介紹了MyBatis使用Zookeeper保存數(shù)據(jù)庫的配置,可動(dòng)態(tài)刷新,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
Java如何利用return結(jié)束方法調(diào)用
這篇文章主要介紹了Java如何利用return結(jié)束方法調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Spring中@Conditional注解的詳細(xì)講解及示例
這篇文章主要介紹了Spring中@Conditional注解的詳細(xì)講解及示例,@Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,滿足條件給容器注冊bean,需要的朋友可以參考下2023-11-11
實(shí)戰(zhàn)分布式醫(yī)療掛號(hào)通用模塊統(tǒng)一返回結(jié)果異常日志處理
這篇文章主要為大家介紹了實(shí)戰(zhàn)分布式醫(yī)療掛號(hào)系統(tǒng)之統(tǒng)一返回結(jié)果統(tǒng)一異常處理,統(tǒng)一日志處理到通用模塊示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-04-04
Mybatis-Plus實(shí)現(xiàn)多主鍵批量保存及更新詳情
這篇文章主要介紹了Mybatis-Plus實(shí)現(xiàn)多主鍵批量保存及更新詳情,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09

