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

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

前言

在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 的构造函数

image.png

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


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


应用程序事件广播器 SimpleApplicationEventMulticaster

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


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

image.png

上面就是将监听者给保存到对象中; (注意: 这里保持的是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) 根据事件类型 找到对应事件的监听类;

image.png

listener.onApplicationEvent(event);


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


加载监听者 ApplicationListener

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


加载时机

image.png

在启动之初,上述就已经把在 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文件中加入

image.png

启动看结果

image.png

可以注意到,我们启动时候传入的入参也是会一起放到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");
    }
    //省略..
}

image.png

自定义事件通知与监听

SpringBoot自定义通知与监听


总结

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



相关文章
|
8天前
|
存储 安全 Java
事件的力量:探索Spring框架中的事件处理机制
事件的力量:探索Spring框架中的事件处理机制
25 0
|
22天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
38 0
|
1月前
|
存储 安全 Java
Spring Boot整合Spring Security--学习笔记
Spring Boot整合Spring Security--学习笔记
52 0
|
1月前
|
消息中间件 Cloud Native Java
【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合
【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合
|
11天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
1月前
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
403 0
|
1天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
4 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
3天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
28 0
【Spring系列】Sping VS Sping Boot区别与联系
|
11天前
|
Java 容器
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
12 0
|
30天前
|
Java Spring
除了spring自带的事件,你还可以这样使用事件
除了spring自带的事件,你还可以这样使用事件
14 3