写点什么

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

  • 2022 年 8 月 05 日
  • 本文字数:3343 字

    阅读完需:约 11 分钟

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

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 中的一个类,事件广播器;他的职责就是把监听到的应用程序事件,广播给所有的监听者; 最终调用监听者ApplicationListeneronApplicationEvent方法;

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

上面就是将监听者给保存到对象中; (注意: 这里保持的是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 总结

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

在这里插入图片描述

发布于: 刚刚阅读数: 3
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 也可获取《kafka运维大全》

评论

发布
暂无评论
【Spring Boot 三】SpringBoot中事件与通知_Spring Boot_石臻臻的杂货铺_InfoQ写作社区