• spring事件监听与发布


    Spring boot

    事件机制重要概念

    在一个事件体系中,有以下几个重要的概念。

    1. 事件源:事件对象的产生者,任何一个EventObject都有一个来源

    2. 事件监听器注册表:当事件框架或组件收到一个事件后,需要通知所有相关的事件监听器来进行处理,这个时候就需要有个存储监听器的地方,也就是事件监听器注册表。事件源与事件监听器关联关系的存储。

    3. 事件广播器:事件广播器在整个事件机制中扮演一个中介的角色,当事件发布者发布一个事件后,就需要通过广播器来通知所有相关的监听器对该事件进行处理。

    下图就是事件机制的结构图
    在这里插入图片描述

    简介

    在使用 Spring 构建的应用程序中,适当使用事件发布与监听的机制可以使我们的代码灵活度更高,降低耦合度Spring 提供了完整的事件发布与监听模型,在该模型中,事件发布方只需将事件发布出去,无需关心有多少个对应的事件监听器;监听器无需关心是谁发布了事件,并且可以同时监听来自多个事件发布方发布的事件,通过这种机制,事件发布与监听是解耦的。

    Spring 事件

    对于SpringApplicationContext(BeanFactory)而言,在整个应用运行过程中(包括应用的启动、销毁),会发布各种应用事件。开发者也可以实现自己的事件,从而起到扩展spring框架的作用 。

    Spring的事件(ApplicationEvent)为 Bean与 Bean之间的消息通信提供了支持。当一个Bean处理完一个任务之后,希望另外一个 Bean知道并能做相应的处理,这时我们就需要让另外一个 Bean监听当前 Bean所发送的事件。

    Spring借助于 org.springframewofk.context.event.ApplicationEvent抽象类及其子类实现事件的发布,与此同时,借助于 org.springframework.context.ApplicationListener接口及其实现者实现事件的监听,这两者构成了观察者 ( observer) 模式。

    监听者模式:

    监听者模式包含了一个监听者Listener与之对应的事件Event,还有一个事件发布者EventPublish,过程就是EventPublish发布一个事件,被监听者捕获到,然后执行事件相应的方法
    
    • 1

    观察者模式

    观察者模式是一对多的模式,一个被观察者Observable和多个观察者Observer,被观察者中存储了所有的观察者对象,当被观察者接收到一个外界的消息,就会遍历广播推送消息给所有的观察者
    
    • 1

    Spring 事件核心接口或类

    序号接口或类描述
    1事件:ApplicationEvent该抽象类继承了EventObject,EventObject是JDK中的类,并建议所有的事件都应该继承自EventObject
    2事件监听器:ApplicationListener是一个接口,该接口继承了EventListener接口。EventListener接口是JDK中的,建议所有的事件监听器都应该继承EventListener。监听器是用于接收事件,并触发事件的操作,这样说起来可能有点费解,简单的说就是,Listener是监听ApplicationContext.publishEvent,方法的调用,一旦调用publishEvent,就会执行ApplicaitonListener中的onApplicationEvent方法
    3事件发布器:ApplicationEventPublisherApplicationContext实现了该接口,在ApplicationContext的抽象实现类AbstractApplicationContext中做了实现,可以通过publishEvent方法发布事件
    4事件源:ApplicationContextApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法:publishEvent(Object event)x在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法。
    5事件广播器:ApplicationEventMulticaster用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

    Spring 事件机制设计类图

    下面我们来看看Spring的事件机制设计类图

    在这里插入图片描述

    Spring 事件执行流程

    1. 当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher的pulishEvent方法发布事件。

    2. 事件广播器ApplicationEventMulticaster通过父类AbstractApplicationEventMulticaster的getApplicationListeners方法去事件注册表ApplicationContext中找到事件监听器ApplicationListener,并且通过invokeListener方法执行监听器的具体逻辑。

    3. 逐个执行监听器ApplicationListener的onApplicationEvent方法,从而完成事件监听器的逻辑。

    在Spring中,使用注册监听接口,除了继承ApplicationListener接口外,还可以使用注解@EventListener来监听一个事件,同时该注解还支持SpEL表达式,来触发监听的条件,比如只接受编码为001的事件,从而实现一些个性化操作。

    Spring 相关事件-内置事件

    Spring 提供了内置事件。Spring 的核心是 ApplicationContext, 当加载 Bean 的时候,ApplicationContext 会发布某些类型的事件,然后通过 ApplicationEventApplicationListener进行处理。

    序号事件描述
    1ContextStartedEvent容器启动的时候触发
    2ContextRefreshedEvent容器初始化或刷新 ApplicationContext 时,将发布此事件
    3ContextStoppedEvent容器停止的时候触发
    4ContextClosedEvent容器关闭的时候触发

    Spring Boot 内置事件

    在Spring Boot的1.5.x中,提供了几种事件,供我们在开发过程中进行更加便捷的扩展及差异化操作。

    序号事件描述
    1ApplicationStartingEventspringboot启动开始的时候执行的事件
    2ApplicationEnvironmentPreparedEventspring boot对应Enviroment已经准备完毕,但此时上下文context还没有创建。在该监听中获取到ConfigurableEnvironment后可以对配置信息做操作,例如:修改默认的配置信息,增加额外的配置信息等等
    3ApplicationPreparedEventspring boot上下文context创建完成,但此时spring中的bean是没有完全加载完成的。在获取完上下文后,可以将上下文传递出去做一些额外的操作。值得注意的是:在该监听器中是无法获取自定义bean并进行操作的
    4ApplicationReadyEventspringboot加载完成时候执行的事件
    5ApplicationFailedEventspring boot启动异常时执行事件

    在这里插入图片描述

    从官网文档中,我们可以知道,由于一些事件是在上下文为加载完触发的,所以无法使用注册bean的方式来声明,文档中可以看出,可以通过SpringApplication.addListeners(…)或者SpringApplicationBuilder.listeners(…)来添加,或者添加META-INF/spring.factories文件中添加监听类也是可以的,这样会自动加载。

    自定义事件

    创建/监听事件应该以下准则

    • 事件类应该继承 ApplicationEvent
    • 事件的发布者应该注入ApplicationEventPublisher
    • 事件监听者应该实现ApplicationListener
    1. 创建事件类 继承 ApplicationEvent

      public class CustomApplicationEvent extends ApplicationEvent {
      
          private String message;
      
          public CustomApplicationEvent(Object source, String message) {
              super(source);
              this.message = message;
          }
      
          public String getMessage() {
              return message;
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    2. 创建事件的发布者 注入ApplicationEventPublisher

      @Slf4j
      @Component
      public class CustomApplicationEventPublisher {
          @Resource
          ApplicationEventPublisher applicationEventPublisher;
      
          public void publishEvent(String message) {
              log.info("开始发布自定义事件");
              CustomApplicationEvent customApplicationEvent = new CustomApplicationEvent(this, message);
              // 发布事件
              applicationEventPublisher.publishEvent(customApplicationEvent);
              log.info("发布自定义事件结束");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    3. 创建事件的监听者实现ApplicationListener接口

    @Slf4j
    @Component
    public class CustomApplicationListener implements ApplicationListener<CustomApplicationEvent> {
        @Override
        public void onApplicationEvent(CustomApplicationEvent event) {
            log.info("onApplicationEvent方法接收到的消息:{}", event.getMessage());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注解驱动

    Spring 4.1后提供了 @EventLister ,不需要手动实现 ApplicationListener接口实现事件的监听,同时也可以配置@Async 使用

    public @interface EventListener {
    
    	@AliasFor("classes")
    	Class<?>[] value() default {};
    
    	@AliasFor("value")
    	Class<?>[] classes() default {};
    
    	String condition() default "";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • value: classes别名
    • classes: 可以指定监听的消息对象类型
    • condition:指定条件下触发事件监听, 当表达式计算结果为true时才触发

    事件监听

    @Slf4j
    @Component
    public class AnnotationCustomApplicationListener {
    
        @EventListener(CustomApplicationEvent.class)
        public void listener(CustomApplicationEvent customApplicationEvent) {
            log.info("EventListener注解方式接收到的消息为:{}", customApplicationEvent.getMessage());
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试

    @SpringBootTest
    public class SpringBootApplicationeventApplicationTests {
    
        @Resource
        private CustomApplicationEventPublisher eventPublisher;
    
        @Test
        public void publishTest() {
            eventPublisher.publishEvent("发布消息");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OOth24Me-1659568419148)(image-20211215101747756.png)]

    异步事件

    Spring 中的事件默认情况下是同步的,发布者线程会进入阻塞状态,直到所有的监听器处理完事件。如果想让事件监听异步执行,需要在监听器上添加@Async, 同时主启动类上添加@EnableAsync注解

    @Slf4j
    @Component
    public class AsynCustomApplicationListener {
    
        @Async
        @EventListener(CustomApplicationEvent.class)
        public void asyncListener(CustomApplicationEvent customApplicationEvent) {
            log.info("异步事件监听,当前线程:{},消息为:{}", Thread.currentThread().getName(), customApplicationEvent.getMessage());
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    同时支持线程池配置

    @EnableAsync
    @Configuration
    public class AsyncTaskExecutorConfig {
        @Bean
        public AsyncTaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(200);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 线程池关闭的时候等待所有任务都完成
            executor.setWaitForTasksToCompleteOnShutdown(true);
            // 设置线程池中任务的等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
            executor.setAwaitTerminationSeconds(60);
            // 如果不初始化,会出现找不到执行器
            executor.initialize();
            return executor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    JS进阶-垃圾回收机制和算法
    拉普拉斯Laplace算子(高斯二阶导核)
    C++ 之 backtrace
    工作几年,如何快速晋升至架构师?
    windows殺死端口
    微信小游戏是个人尝试做游戏最好的选择
    基于Python实现的价值迭代(Value Iterator)
    Linux 开机启动一条PHP命令
    Java多线程 信号量和屏障实现控制并发线程数量,主线程等待所有线程执行完毕2
    4.每天进步一点点-Python爬虫需要了解HTTP 响应报文
  • 原文地址:https://blog.csdn.net/Smy_0114/article/details/126152214