Part1 前言
在 SpringBoot 启动过程中,有下面两行代码
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
复制代码
简单概括来说,他的作用是:
通过 spring.factories 文件中找到所有需要被实例化的SpringApplicationRunListener
的实现类;并将其实例化,然后执行starting
方法; 在 SpringBoot 中这个实现类只有EventPublishingRunListener
; 这就涉及到了 Spring 中的事件与通知机制了
Part2 正文源码解析
1 事件发布监听器 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
的构造函数
特别说明一下: SpringApplicationRunListener
的实现类的构造函数必须是 上面两个参数;如果没有对应的构造函数的话会报错;这是因为在实例化这个对象的时候特别指定的是这两个参数的构造函数,如果找不到就实例化不了;
我们可以看到这个类主要的一个属性是 SimpleApplicationEventMulticaster
;
应用程序事件广播器 SimpleApplicationEventMulticaster
这是 Spring 中的一个类,事件广播器;他的职责就是把监听到的应用程序事件,广播给所有的监听者; 最终调用监听者ApplicationListener
的onApplicationEvent
方法;
既然要广播给所有的监听者,那得要知道有哪些监听者,上面
上面就是将监听者给保存到对象中; (注意: 这里保持的是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)
根据事件类型 找到对应事件的监听类;
listener.onApplicationEvent(event);
那么这些 listeners 从哪里来的呢 -> application.getListeners()
加载监听者 ApplicationListener
ApplicationListener 是一个接口,只有一个方法onApplicationEvent(E even)
; 每个实现类要实现这个方法; 入参 E 是一个事件类 ApplicationEvent; 将实现类配置到spring.factories
之后就会被 SimpleApplicationEventMulticaster
广播着管理;后续有事件发生就会通知到实现类;
加载时机
在启动之初,上述就已经把在 spring.factories
中找到的所有ApplicationListener
给实例化了;并将其设置到属性List<ApplicationListener<?>> listeners;
中;
2 扩展
既然我们知道了 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
文件中加入
启动看结果
可以注意到,我们启动时候传入的入参也是会一起放到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");
}
//省略..
}
复制代码
在这里插入图片描述
自定义事件通知与监听
SpringBoot自定义通知与监听
Part3 总结
在这里插入图片描述
评论