前言


在SpringBoot启动过程中,有下面两行代码

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

简单概括来说,他的作用是:


通过spring.factories文件中找到所有需要被实例化的​​SpringApplicationRunListener​​​的实现类;并将其实例化,然后执行​​starting​​​方法; 在SpringBoot中这个实现类只有​​EventPublishingRunListener​​; 这就涉及到了Spring中的事件与通知机制了


正文源码解析


事件发布监听器 EventPublishingRunListener


这个类是SpringBoot用来监听Spring运行过程事件,监听到对应的事件之后,它会把对应的事件广播出去;


这个类实现了​​SpringApplicationRunListener​​接口;具体的事件有以下

/**在首次启动run方法时立即调用。可用于非常*早期的初始化。 */
void starting();
/**在环境准备好之后,但在创建* {@link ApplicationContext}之前调用。*/
void environmentPrepared(ConfigurableEnvironment environment);
/**在创建和准备{@link ApplicationContext}之后调用,但在加载源之前调用*。 * @param context应用程序上下文*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);

/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
void started(ConfigurableApplicationContext context);

/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
void running(ConfigurableApplicationContext context);

/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
void failed(ConfigurableApplicationContext context, Throwable exception);

在来看看​​EventPublishingRunListener​​ 的构造函数

【Spring Boot 三】SpringBoot中事件与通知_实例化

特别说明一下: SpringApplicationRunListener的实现类的构造函数必须是 上面两个参数;如果没有对应的构造函数的话会报错;这是因为在实例化这个对象的时候特别指定的是这两个参数的构造函数,如果找不到就实例化不了;

我们可以看到这个类主要的一个属性是 ​​SimpleApplicationEventMulticaster​​;

应用程序事件广播器 SimpleApplicationEventMulticaster


这是Spring中的一个类,事件广播器;他的职责就是把监听到的应用程序事件,广播给所有的监听者; 最终调用监听者​​ApplicationListener​​​的​​onApplicationEvent​​方法;


既然要广播给所有的监听者,那得要知道有哪些监听者,上面

【Spring Boot 三】SpringBoot中事件与通知_构造函数_02

上面就是将监听者给保存到对象中; (注意: 这里保持的是​​spring.factories方式获取到的,但是获取的时候 还会读取被Spring管理的ApplicationListener的bean; 可是能不能读取到 Spring管理的bean要看事件触发的时机,比如 staring事件,这个时间Spring容器都还没有初始化,那些被注解管理的ApplicationListener就不会被读取到​​)

然后有事件发生时候最终会调用

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}

​getApplicationListeners(event, type)​​ 根据事件类型 找到对应事件的监听类;

【Spring Boot 三】SpringBoot中事件与通知_实例化_03

​listener.onApplicationEvent(event);​

那么这些listeners从哪里来的呢 -> ​​application.getListeners()​

加载监听者 ApplicationListener


ApplicationListener是一个接口,只有一个方法​​onApplicationEvent(E even)​​​; 每个实现类要实现这个方法; 入参E是一个事件类ApplicationEvent; 将实现类配置到​​spring.factories​​​之后就会被 ​​SimpleApplicationEventMulticaster​​广播着管理;后续有事件发生就会通知到实现类;


加载时机

【Spring Boot 三】SpringBoot中事件与通知_构造函数_04

在启动之初,上述就已经把在 ​​spring.factories​​ 中找到的所有​​ApplicationListener​​给实例化了;

并将其设置到属性​​List<ApplicationListener<?>> listeners;​​中;

扩展

既然我们知道了Spring中的事件与通知机制,那么我们是否能做一些扩展了

SpringBoot开始启动的时候 打印一下日志

我们已经知道启动的方法在 ​​SpringApplicationRunListener.starting()​​ ;

public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}

① 继承一个ApplicationListener接口

public class StartApplicationListenerInFactories implements ApplicationListener<ApplicationStartingEvent>, Ordered {

@Override
public void onApplicationEvent(ApplicationStartingEvent event) {

System.out.println("应用程序开始启动-监听器-----在Factories中方式;args.name:");
Arrays.asList(event.getArgs()).stream().forEach(x -> System.out.println(x));
}

@Override
public int getOrder() {
return 0;
}
}

并在​​spring.factories​​文件中加入

【Spring Boot 三】SpringBoot中事件与通知_源码_05

启动看结果

【Spring Boot 三】SpringBoot中事件与通知_源码_06

可以注意到,我们启动时候传入的入参也是会一起放到​​SpringApplicationEvent​​中的;

如果不在​​spring.factories​​中配置,直接用注解被管理可以吗


不可以,在应用程序刚开始启动的时候,Spring容器都还没有初始化,是选择用​​spring.factories​​方式还是用注解管理的方式,需要看调用的时机; 像staring这个时机就不行;


那哪些时机可以? TODO… 单独文章分析

②. 实现一个SpringApplicationRunListener类

上面一种方式 是用SpringBoot内部的通知类​​EventPublishingRunListener​​ 来通知到所有监听对应事件的监听者; 所以上面的方式只需要写一个监听者监听对应的事件就行了;

​EventPublishingRunListener​​ 是实现了类​​SpringApplicationRunListener​​的实现类;那么我们也可以直接实现一个 ​​SpringApplicationRunListener​​类呀;

public class MyRunListener implements SpringApplicationRunListener, Ordered {
//注意 SpringApplicationRunListener 的构造函数必须带有下面两个参数; 因为在实例化的时候查找的构造函数指定了这两个参数的
public MyRunListener(SpringApplication application, String[] args) {
}

@Override
public void starting() {

System.out.println("MyRunListener.......staring");
}
//省略..
}

【Spring Boot 三】SpringBoot中事件与通知_实例化_07

自定义事件通知与监听

​​SpringBoot自定义通知与监听​​

总结

  • SpringApplicationRunListener 定义了应用程序启动的过程每个节点事件; SpringBoot会将每个节点事件通知给监听者们Listeners ;开发者可以实现接口​​ApplicationListener​​ 来实现自己的逻辑;

【Spring Boot 三】SpringBoot中事件与通知_spring_08