一、前言
在Spring Boot的启动过程中,会触发一系列的事件,这些事件为开发者提供了在特定阶段扩展系统功能的机会。本文将介绍Spring Boot的启动过程中的一些事件,以及知道这些事件,我们能做什么。
二、启动过程中的事件以及作用
1. ApplicationStartingEvent:
触发时机:在Spring Boot应用程序启动的最早阶段触发。
用途:此时,应用程序刚刚启动,可能正在加载配置文件和设置。
源码
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
作用
在这个阶段,你可以进行日志初始化、系统参数设置等准备工作。
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;
public class ApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
// 在这里进行日志初始化、系统参数设置等操作
System.out.println("Application is starting...");
}
}
// 注册监听器
// 通常通过Spring的组件扫描自动注册,或者使用@Bean在配置类中注册
2. ApplicationEnvironmentPreparedEvent:
触发时机:在Environment对象创建之后,Context对象创建之前抛出。
用途:此时,应用程序的环境已经准备好,但Spring上下文还未被创建和初始化。
源码
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
作用
你可以在这个阶段访问和修改应用程序的环境属性
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
public class ApplicationEnvironmentPreparedEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
// 在这里访问和修改环境属性
String customProperty = environment.getProperty("custom.property");
System.out.println("Custom property: " + customProperty);
// ...
}
}
3. ApplicationContextInitializedEvent:
触发时机:在ApplicationContext对象被初始化后抛出,但所有bean的定义还没被加载。
用途:允许在bean定义加载之前对ApplicationContext进行初始化操作。
源码
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
// 触发ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
应用
假设我们想在 ApplicationContext 初始化时,基于 Environment 的某些属性来动态注册一些 BeanDefinition。
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
// 假设我们有一个名为 custom.feature.enabled 的属性
String featureEnabled = environment.getProperty("custom.feature.enabled");
if ("true".equals(featureEnabled)) {
// 基于该属性,我们可以动态地注册一个 bean 到 Spring 上下文中
// 这里只是一个示例,实际上你需要使用 BeanDefinitionRegistry 来注册 bean
// ...
// 另一种选择是设置一些属性或执行其他初始化任务
System.out.println("Custom feature is enabled. Performing additional initialization...");
}
}
}
4. ApplicationPreparedEvent:
触发时机:在bean定义被加载之后,Context对象刷新之前抛出。
用途:此时,Spring的bean定义已经加载,但Spring上下文还未完全初始化。
源码
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 触发事件
listeners.contextLoaded(context);
}
应用
@Component
public class ApplicationPreparedEventListener implements ApplicationListener<ApplicationPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
System.out.println("ApplicationPreparedEvent触发:ApplicationContext已创建,但bean还未加载。");
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
// 在这里可以向ApplicationContext中添加自定义的bean定义
}
}
5.ServletWebServerInitializedEvent:
触发时机:当内嵌的Servlet Web服务器初始化完成时触发。
用途:获取关于Web服务器的信息,例如监听端口等。
源码
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
应用
@Component
public class ServletWebServerInitializedEventListener implements ApplicationListener<ServletWebServerInitializedEvent> {
@Override
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
System.out.println("ServletWebServerInitializedEvent触发:内嵌Web服务器已初始化完成。");
WebServer webServer = event.getWebServer();
int port = webServer.getPort();
// 在这里可以获取Web服务器的端口号、设置HTTP监听器等
}
}
6. ContextRefreshedEvent:
触发时机:在ApplicationContext被初始化或刷新时触发。
用途:这通常意味着所有的bean都已经加载,并且可以进行后处理操作。
源码
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
应用
假设我们有一个需要在 Spring 容器初始化后执行的操作,比如加载一些静态数据到缓存中。我们可以创建一个监听 ContextRefreshedEvent 的类,并在该类中执行我们需要的操作。
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
// 在这里执行你需要的操作,比如加载数据到缓存
System.out.println("ContextRefreshedEvent received. Loading data into cache...");
// 假设我们有一个服务,用于加载数据到缓存
MyDataService myDataService = applicationContext.getBean(MyDataService.class);
myDataService.loadDataToCache();
}
}
7. ApplicationStartedEvent:
触发时机:在Context对象刷新之后,应用启动器被调用之前抛出。
用途:此时,应用程序已经启动,但还未准备好接收HTTP请求。
源码
public ConfigurableApplicationContext run(String... args) {
...
refreshContext(context);
// 容器已经初始化完成,发布该事件
listeners.started(context);
return context;
}
应用
假设我们想在 Spring Boot 应用程序启动后执行一些自定义的初始化操作,比如启动一个后台任务。我们可以创建一个监听 ApplicationStartedEvent 的类,并在该类中执行我们需要的操作
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
public class MyApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 在这里执行你需要的操作,比如启动一个后台任务
System.out.println("ApplicationStartedEvent received. Starting background task...");
// 假设我们有一个服务,用于启动后台任务
MyBackgroundTaskService myBackgroundTaskService = new MyBackgroundTaskService();
myBackgroundTaskService.start();
}
}
8. ApplicationReadyEvent:
触发时机:在应用启动器被调用之后抛出,这代表着应用已经被正常的启动,可以接收请求了。
用途:这是应用程序启动的最后阶段,此时应用程序已经完全准备好,可以开始处理请求。
源码
public ConfigurableApplicationContext run(String... args) {
// 刷新上下文
refreshContext(context);
try {
// 触发ApplicationReadyEvent事件
listeners.ready(context, timeTakenToReady);
}
}
应用
@Component
public class ApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("ApplicationReadyEvent触发:Spring Boot应用程序已准备好服务请求。");
// 在这里可以执行一些需要在应用程序完全就绪后才能进行的操作
}
}
9.ApplicationFailedEvent:
触发时机:当应用程序启动失败时触发。
用途:通知监听器应用程序启动失败,并可能包含关于失败原因的详细信息。
源码
// 刷新上下文
refreshContext(context);
try {
// 当这里发生了异常
listeners.ready(context, timeTakenToReady);
} catch (Throwable ex) {
// 发布ApplicationFailedEvent事件
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
这些事件是Spring Boot生命周期中的关键节点,允许开发者在特定的时间点执行自定义逻辑,如初始化资源、加载配置、设置监听器等。通过实现ApplicationListener接口并注册相应的监听器,开发者可以监听这些事件并在事件发生时执行特定的操作。