
Pre
Spring Boot - Application Events 的發(fā)布順序_ApplicationEnvironmentPreparedEvent
概述
Spring Boot 的廣播機(jī)制是基于觀察者模式實(shí)現(xiàn)的,它允許在 Spring 應(yīng)用程序中發(fā)布和監(jiān)聽(tīng)事件。這種機(jī)制的主要目的是為了實(shí)現(xiàn)解耦,使得應(yīng)用程序中的不同組件可以獨(dú)立地改變和復(fù)用邏輯,而無(wú)需直接進(jìn)行通信。
在 Spring Boot 中,事件發(fā)布和監(jiān)聽(tīng)的機(jī)制是通過(guò) ApplicationEvent
、ApplicationListener
以及事件發(fā)布者(ApplicationEventPublisher
)來(lái)實(shí)現(xiàn)的。其中,ApplicationEvent 是所有自定義事件的基礎(chǔ),自定義事件需要繼承自它。
ApplicationListener
是監(jiān)聽(tīng)特定事件并做出響應(yīng)的接口,開(kāi)發(fā)者可以通過(guò)實(shí)現(xiàn)該接口來(lái)定義自己的監(jiān)聽(tīng)器。事件發(fā)布者(通常由 Spring 的 ApplicationContext
擔(dān)任)負(fù)責(zé)發(fā)布事件。
在Spring框架中,ApplicationFailedEvent
是一個(gè)特殊的事件,它代表了應(yīng)用程序在啟動(dòng)過(guò)程中遇到的失敗情況。這個(gè)事件是在Spring的應(yīng)用程序生命周期中,當(dāng)應(yīng)用程序啟動(dòng)失敗時(shí)觸發(fā)的。
ApplicationFailedEvent
事件通常包含了有關(guān)失敗原因的信息,例如異常類(lèi)型、異常消息、發(fā)生錯(cuò)誤的類(lèi)和方法、以及失敗發(fā)生的時(shí)間等。這個(gè)事件是Spring事件機(jī)制的一部分,它允許開(kāi)發(fā)者在應(yīng)用程序中實(shí)現(xiàn)事件驅(qū)動(dòng)的設(shè)計(jì)。
在Spring框架中,事件機(jī)制是基于觀察者模式的實(shí)現(xiàn)。事件發(fā)布者和事件監(jiān)聽(tīng)器通過(guò)事件進(jìn)行通信。在Spring中,事件發(fā)布者通常是通過(guò) ApplicationEventPublisher
接口來(lái)進(jìn)行操作的,而事件監(jiān)聽(tīng)器則通過(guò)實(shí)現(xiàn) ApplicationListener
接口來(lái)定義。
當(dāng)Spring應(yīng)用程序啟動(dòng)時(shí),它會(huì)經(jīng)歷多個(gè)階段。如果在某個(gè)階段發(fā)生了錯(cuò)誤,比如在初始化數(shù)據(jù)源時(shí)出現(xiàn)了異常,Spring會(huì)發(fā)布 ApplicationFailedEvent
事件。事件監(jiān)聽(tīng)器可以監(jiān)聽(tīng)這個(gè)事件,并對(duì)事件進(jìn)行處理,比如記錄日志、發(fā)送警報(bào)或者進(jìn)行補(bǔ)償操作等。
在Spring Boot應(yīng)用程序中,ApplicationFailedEvent
事件也可以被用來(lái)處理啟動(dòng)時(shí)的異常情況。Spring Boot提供了一種更簡(jiǎn)化的方式來(lái)監(jiān)聽(tīng)這個(gè)事件,即使用 @EventListener
注解。這種方式可以讓開(kāi)發(fā)者更容易地編寫(xiě)事件監(jiān)聽(tīng)器,而不需要實(shí)現(xiàn)復(fù)雜的接口。
例如,以下是一個(gè)簡(jiǎn)單的 @EventListener
注解的使用示例,用于監(jiān)聽(tīng) ApplicationFailedEvent
事件:
@Component
public class ApplicationFailedListener {
@EventListener
public void onApplicationFailedEvent(ApplicationFailedEvent event) {
Throwable throwable = event.getException();
// 對(duì)異常進(jìn)行處理,比如記錄日志
System.err.println("Application failed to start: " + throwable.getMessage());
}
}
當(dāng)應(yīng)用程序啟動(dòng)失敗時(shí),這個(gè)監(jiān)聽(tīng)器會(huì)被觸發(fā),并可以執(zhí)行相應(yīng)的錯(cuò)誤處理邏輯。這樣,開(kāi)發(fā)者可以更好地管理應(yīng)用程序的啟動(dòng)過(guò)程,并在遇到失敗時(shí)進(jìn)行適當(dāng)?shù)捻憫?yīng)。
Code
package com.artisan.event;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
public class ApplicationFailedListener implements ApplicationListener<ApplicationFailedEvent> {
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
System.out.println("--------------------> Handling ApplicationFailedEvent here!");
Throwable throwable = event.getException();
// 對(duì)異常進(jìn)行處理,比如記錄日志
System.err.println("Application failed to start: " + throwable.getMessage());
}
}
如何使用呢?
方式一:
@SpringBootApplication
public class LifeCycleApplication {
/**
* 除了手工add , 在 META-INF下面 的 spring.factories 里增加
* org.springframework.context.ApplicationListener=自定義的listener 也可以
*
* @param args
*/
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(LifeCycleApplication.class);
springApplication.addListeners(new ApplicationFailedListener());
springApplication.run(args);
}
}
方式二: 通過(guò)spring.factories 配置
org.springframework.context.ApplicationListener=\
com.artisan.event.ApplicationFailedListener
運(yùn)行日志
源碼分析
首先main方法啟動(dòng)入口
SpringApplication.run(LifeCycleApplication.class, args);
跟進(jìn)去
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
繼續(xù)
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
這里首先關(guān)注 new SpringApplication(primarySources)
new SpringApplication(primarySources)
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
聚焦 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
run
繼續(xù)run
// 開(kāi)始啟動(dòng)Spring應(yīng)用程序
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); // 創(chuàng)建一個(gè)計(jì)時(shí)器
stopWatch.start(); // 開(kāi)始計(jì)時(shí)
DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 創(chuàng)建引導(dǎo)上下文
ConfigurableApplicationContext context = null; // Spring應(yīng)用上下文,初始化為null
configureHeadlessProperty(); // 配置無(wú)頭屬性(如:是否在瀏覽器中運(yùn)行)
SpringApplicationRunListeners listeners = getRunListeners(args); // 獲取運(yùn)行監(jiān)聽(tīng)器
listeners.starting(bootstrapContext, this.mainApplicationClass); // 通知監(jiān)聽(tīng)器啟動(dòng)過(guò)程開(kāi)始
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 創(chuàng)建應(yīng)用參數(shù)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 預(yù)備環(huán)境
configureIgnoreBeanInfo(environment); // 配置忽略BeanInfo
Banner printedBanner = printBanner(environment); // 打印Banner
context = createApplicationContext(); // 創(chuàng)建應(yīng)用上下文
context.setApplicationStartup(this.applicationStartup); // 設(shè)置應(yīng)用啟動(dòng)狀態(tài)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 準(zhǔn)備上下文
refreshContext(context); // 刷新上下文,執(zhí)行Bean的生命周期
afterRefresh(context, applicationArguments); // 刷新后的操作
stopWatch.stop(); // 停止計(jì)時(shí)
if (this.logStartupInfo) { // 如果需要記錄啟動(dòng)信息
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); // 記錄啟動(dòng)信息
}
listeners.started(context); // 通知監(jiān)聽(tīng)器啟動(dòng)完成
callRunners(context, applicationArguments); // 調(diào)用Runner
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners); // 處理運(yùn)行失敗
throw new IllegalStateException(ex); // 拋出異常
}
try {
listeners.running(context); // 通知監(jiān)聽(tīng)器運(yùn)行中
}
catch (Throwable ex) {
handleRunFailure(context, ex, null); // 處理運(yùn)行失敗
throw new IllegalStateException(ex); // 拋出異常
}
return context; // 返回應(yīng)用上下文
}
我們重點(diǎn)看
handleRunFailure(context, ex, listeners); // 處理運(yùn)行失敗
繼續(xù)
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
}
finally {
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
繼續(xù) listeners.failed(context, exception);
void failed(ConfigurableApplicationContext context, Throwable exception) {
doWithListeners("spring.boot.application.failed",
(listener) -> callFailedListener(listener, context, exception), (step) -> {
step.tag("exception", exception.getClass().toString());
step.tag("message", exception.getMessage());
});
}
繼續(xù) callFailedListener;
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
try {
listener.failed(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
context.publishEvent(event);
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
繼續(xù)this.initialMulticaster.multicastEvent(event);
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
繼續(xù)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 如果eventType不為null,則直接使用它;否則,使用resolveDefaultEventType方法來(lái)解析事件的默認(rèn)類(lèi)型。
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 獲取一個(gè)線(xiàn)程池執(zhí)行器,它用于異步執(zhí)行監(jiān)聽(tīng)器調(diào)用。
Executor executor = getTaskExecutor();
// 獲取所有對(duì)應(yīng)該事件類(lèi)型的監(jiān)聽(tīng)器。
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果執(zhí)行器不為null,則使用它來(lái)異步執(zhí)行監(jiān)聽(tīng)器調(diào)用;
// 否則,直接同步調(diào)用監(jiān)聽(tīng)器。
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
繼續(xù)
/**
* 調(diào)用一個(gè)事件監(jiān)聽(tīng)器的方法。
*
* @param listener 要調(diào)用的監(jiān)聽(tīng)器
* @param event 要處理的事件
*/
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 直接調(diào)用監(jiān)聽(tīng)器的onApplicationEvent方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
// 如果消息為null或者消息匹配事件類(lèi)的預(yù)期類(lèi)型,則忽略異常并記錄debug日志
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
// 否則,拋出異常
else {
throw ex;
}
}
}
繼續(xù) 就會(huì)調(diào)到我們自己的業(yè)務(wù)邏輯了文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-795312.html
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
System.out.println("--------------------> Handling ApplicationFailedEvent here!");
Throwable throwable = event.getException();
// 對(duì)異常進(jìn)行處理,比如記錄日志
System.err.println("Application failed to start: " + throwable.getMessage());
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-795312.html
到了這里,關(guān)于Spring Boot - Application Events 的發(fā)布順序_ApplicationFailedEvent的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!