文章目录
Pre
Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent
Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent
概述
Spring Boot 的广播机制是基于观察者模式实现的,它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦,使得应用程序中的不同组件可以独立地改变和复用逻辑,而无需直接进行通信。
在 Spring Boot 中,事件发布和监听的机制是通过 ApplicationEvent
、ApplicationListener
以及事件发布者(ApplicationEventPublisher
)来实现的。其中,ApplicationEvent 是所有自定义事件的基础,自定义事件需要继承自它。
ApplicationListener
是监听特定事件并做出响应的接口,开发者可以通过实现该接口来定义自己的监听器。事件发布者(通常由 Spring 的 ApplicationContext
担任)负责发布事件。
ApplicationContextInitializedEvent
是Spring框架中的一个事件,它在Spring应用上下文(ApplicationContext)初始化完成,但还未启动时触发。这个事件是在Spring框架初始化过程中,ApplicationContext对象创建完成,但还未开始加载 beans 和 配置之前发布的。
在Spring框架中,ApplicationContext
是核心接口,负责实例化、配置和组装Bean。ApplicationContextInitializedEvent
事件可以被用于执行一些需要在Spring应用上下文完全初始化,但是Bean尚未加载时的初始化代码。
使用场景举例:
-
自定义初始化逻辑 :
当需要在Spring应用上下文初始化后,但Bean加载之前执行特定逻辑时,比如设置共享资源、初始化配置信息等。
-
监听应用上下文初始化完成 :
用于在Spring应用上下文完全初始化后立即执行某些操作,比如日志记录、系统参数配置等。
-
插件或扩展点 :
对于需要扩展Spring框架功能的第三方插件,可以在监听到这个事件时,进行一些自定义操作,如添加额外的Bean定义,或者修改已有的Bean定义。
-
资源加载与配置 :
如果需要在Spring上下文初始化后,但Bean创建之前加载某些资源(如数据库连接、外部配置文件等),这个事件可以提供这样的机会。
在实际使用中,可以通过实现ApplicationListener
接口来监听ApplicationContextInitializedEvent
事件。
java
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextInitializedEvent;
public class CustomInitializationListener implements ApplicationListener<ApplicationContextInitializedEvent> {
@Override
public void onApplicationEvent(ApplicationContextInitializedEvent event) {
// 执行初始化逻辑
}
}
然后,需要在Spring配置文件中注册这个监听器,或者使用注解@Component
进行自动注册。
需要注意的是,在Spring Boot项目中,事件监听通常更加自动化,并且通常不需要手动注册监听器。Spring Boot会自动配置并注册事件监听器,开发者只需关注事件的处理逻辑即可。
Code
java
package com.artisan.event;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
@Configuration
public class ApplicationContextNewInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
/**
* ApplicationContextInitializedEvent 在准备应用程序上下文期间,但在将 Bean 定义加载到 Spring 容器之前。
* <p>
* 此事件提供了在初始化 Bean 之前执行任务的机会,例如注册属性源和基于上下文环境激活 Bean 等。
* <p>
* <p>
* 为了处理该 ApplicationContextInitializedEvent 事件,
* 我们可以通过实现 ApplicationContextInitializer ConfigurableApplicationContext 作为泛型类型的接口来为应用程序创建一个额外的初始值设定项。
* 可以在主应用程序类中手动添加此初始值设定项。
* <p>
* <p>
* 当我们运行 Spring Boot 应用程序时, ApplicationContextNewInitializer 将调用 这将允许我们在加载任何 Bean 定义之前根据需要执行任务
* new SpringApplicationBuilder(EventsApplication.class).initializers(new ApplicationContextNewInitializer()).run(args);
*
* @param applicationContext the application to configure
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("--------------------> Handling ApplicationContextInitializedEvent here!");
}
}
如何使用呢?
方式一:
java
@SpringBootApplication
public class LifeCycleApplication {
/**
* 除了手工add , 在 META-INF下面 的 spring.factories 里增加
* org.springframework.context.ApplicationListener=自定义的listener 也可以
*
* @param args
*/
public static void main(String[] args) {
new SpringApplicationBuilder(LifeCycleApplication.class)
.initializers(new ApplicationContextNewInitializer()).run(args)
}
}
方式二: 通过spring.factories 配置
xml
org.springframework.context.ApplicationContextInitializer=\
com.artisan.event.ApplicationContextNewInitializer
运行日志
源码分析
首先main方法启动入口
java
SpringApplication.run(LifeCycleApplication.class, args);
跟进去
java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
继续
java
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里首先关注 new SpringApplication(primarySources)
new SpringApplication(primarySources)
java
/**
* 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
继续run
java
// 开始启动Spring应用程序
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); // 创建一个计时器
stopWatch.start(); // 开始计时
DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 创建引导上下文
ConfigurableApplicationContext context = null; // Spring应用上下文,初始化为null
configureHeadlessProperty(); // 配置无头属性(如:是否在浏览器中运行)
SpringApplicationRunListeners listeners = getRunListeners(args); // 获取运行监听器
listeners.starting(bootstrapContext, this.mainApplicationClass); // 通知监听器启动过程开始
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建应用参数
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 预备环境
configureIgnoreBeanInfo(environment); // 配置忽略BeanInfo
Banner printedBanner = printBanner(environment); // 打印Banner
context = createApplicationContext(); // 创建应用上下文
context.setApplicationStartup(this.applicationStartup); // 设置应用启动状态
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 准备上下文
refreshContext(context); // 刷新上下文,执行Bean的生命周期
afterRefresh(context, applicationArguments); // 刷新后的操作
stopWatch.stop(); // 停止计时
if (this.logStartupInfo) { // 如果需要记录启动信息
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); // 记录启动信息
}
listeners.started(context); // 通知监听器启动完成
callRunners(context, applicationArguments); // 调用Runner
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners); // 处理运行失败
throw new IllegalStateException(ex); // 抛出异常
}
try {
listeners.running(context); // 通知监听器运行中
}
catch (Throwable ex) {
handleRunFailure(context, ex, null); // 处理运行失败
throw new IllegalStateException(ex); // 抛出异常
}
return context; // 返回应用上下文
}
我们重点看
java
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
继续
java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将环境变量设置到Spring上下文
context.setEnvironment(environment);
// 对Spring上下文进行后处理
postProcessApplicationContext(context);
// 应用初始izers,这些是对Spring上下文进行额外配置的组件
applyInitializers(context);
// 通知监听器,上下文已准备好
listeners.contextPrepared(context);
// 关闭bootstrap上下文
bootstrapContext.close(context);
// 如果需要记录启动信息
if (this.logStartupInfo) {
// 记录启动信息,并判断是否为根上下文
logStartupInfo(context.getParent() == null);
// 记录Spring Boot的配置信息
logStartupProfileInfo(context);
}
// 注册Spring Boot特定的单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 注册应用启动参数为单例bean,键为'springApplicationArguments'
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 如果有打印的Banner,将其注册为单例bean,键为'springBootBanner'
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 如果bean工厂是DefaultListableBeanFactory的实例,设置是否允许Bean定义覆盖
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果设置了懒惰初始化,添加一个后处理器
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 加载所有源,通常是Bean定义的来源
Set<Object> sources = getAllSources();
// 断言源集合不为空,这些源将被加载到Spring上下文中
Assert.notEmpty(sources, "Sources must not be empty");
// 使用源数组加载Spring上下文
load(context, sources.toArray(new Object[0]));
// 通知监听器,上下文已加载
listeners.contextLoaded(context);
}
【applyInitializers】
java
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
就到了我们自定义实现的代码逻辑中了。
java
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("--------------------> Handling ApplicationContextInitializedEvent here!");
}
继续
java
listeners.contextPrepared(context);
又看了熟悉的
java
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
继续
java
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
java
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 如果eventType不为null,则直接使用它;否则,使用resolveDefaultEventType方法来解析事件的默认类型。
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取一个线程池执行器,它用于异步执行监听器调用。
Executor executor = getTaskExecutor();
// 获取所有对应该事件类型的监听器。
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果执行器不为null,则使用它来异步执行监听器调用;
// 否则,直接同步调用监听器。
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
继续
java
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
......
}
}