Spring Boot 的事件发布与监听

2022-07-21 12:25:26

事件监听机制我理解的应该是类似于一种观察者模式。利用事件的监听机制可以实现业务的发起与业务的处理解耦。说白了更能体现单依职责原则 想象一下如果一个用户触发了某个动作需要发送邮件, 那么发出动作的一方一定是在 用户相关的类, 发送邮件的动作一定是在邮件相关的类, 这种情况下用户类就需要依赖邮件类, 形成耦合. 如果用事件来解决这个问题, 用户只需要发起一个相关的事件, 至于谁来处理这个事件, 用户类不需要关心, 而邮件类只需要监听这个事件, 至于谁发起的这个事件, 邮件类也不需要关心.单依职责原则, 最少知识原则

基于 Spring 实现事件发布与监听需要三步:

  • 定义事件
  • 发布事件
  • 监听事件

定义事件

  1. 继承ApplicationEvent 定义事件类

新建一个类,随便起个名字,最好是xxxEvent 这种命名。然后继承ApplicationEvent 这个类即可

publicclassCrewEventextendsApplicationEvent{@GetterprivatefinalString name;/**
     * 创建一个新的 {@code ApplicationEvent}.
     *
     * @param source 事件源
     * @param name 自定义的属性,根据自己的业务需要添加
     */publicCrewEvent(Object source,String name){super(source);this.name= name;}}
  1. Spring 4.2.x 及以后的版本可以不用继承ApplicationEvent 类, 任何一个普通的Java Bean 都可以当做 事件类
publicclassCrewEvent{@GetterprivatefinalString name;@GetterprivatefinalObject source;/**
     * 创建一个新的 {@code ApplicationEvent}.
     *
     * @param source 事件源
     * @param name 自定义的属性,根据自己的业务需要添加
     */publicCrewEvent(Object source,String name){this.name= name;this.source= source;}}

发布事件

发布事件只需要调用ApplicationEventPublisherpublishEvent 方法即可

@RestController@AllArgsConstructor@Slf4jpublicclassReportController{privatefinalApplicationEventPublisher applicationEventPublisher;@GetMapping("/report")publicStringgetView(){
        applicationEventPublisher.publishEvent(newCrewEvent(this,"report"));return"事件已经触发";}}

事件监听

  1. 基于注解监听

Spring 提供了基于注解的事件监听方法只需要在Spring Bean 的方法上加上@EventListener 即可.

EventListener 可以传入参数来判断被修饰的方法监听哪个事件类

还可以添加@Async 注解, 使其可以被异步的执行

@Component@Slf4jpublicclassCrewEventListener{@Async@EventListener(CrewEvent.class)publicvoidcrewEventHandle(CrewEvent event){
        log.info("event.name: {}", event.getName());
        log.info("event.source: {}", event.getSource());}}
  1. 基于接口监听

基于接口的监听需要实现ApplicationListener 接口在泛型参数中填入要监听的事件类, 并实现onApplicationEvent 方法即可

接口泛型中的时间类会被当做参数传递给onApplicationEvent 方法

@Component@Slf4jpublicclassCrewEventListenerimplementsApplicationListener<CrewEvent>{@OverridepublicvoidonApplicationEvent(CrewEvent event){
        log.info("event.name: {}", event.getName());
        log.info("event.source: {}", event.getSource());System.out.println(event.getTimestamp());}}

Spring Boot 的启动事件

Spring Boot 在启动的过程中也会发布一系列的事件, 例如: 在应用启动且未作任何处理时会发布ApplicationStartingEvent 事件, 在确定Spring Boot应用使用的 Environment 且 context 创建之前发送ApplicationEnvironmentPreparedEvent事件. 对这些事件进行监听可以实现一些特殊的效果, 比如在程序启动时做一些启动一些工作线程, 加载一些系统参数等.

下面进行详细说明

Spring Boot 启动会触发哪些事件

ApplicationStartingEvent :应用启动且未作任何处理(除listener注册和初始化)
ApplicationEnvironmentPreparedEvent: 确定springboot应用使用的Environment且context创建之前
ApplicationPreparedEvent:context已经创建且没有refresh发送个事件
ApplicationStartedEvent: context已经refresh且application and command-line runners(如果有)调用之前发送这个事件
ApplicationReadyEvent: pplication and command-line runners (如果有)执行完后发送这个事件,此时应用已经启动完毕
ApplicationFailedEvent: 应用启动失败后产生这个事件

监听Spring Boot 启动事件

publicclassCrewSystemEventListenerimplementsApplicationListener<ApplicationEvent>{@OverridepublicvoidonApplicationEvent(ApplicationEvent event){//springboot应用启动且未作任何处理(除listener注册和初始化)的时候发送ApplicationStartingEventif(eventinstanceofApplicationStartingEvent){System.out.println("ApplicationStarting");}//确定springboot应用使用的Environment且context创建之前发送这个事件if(eventinstanceofApplicationEnvironmentPreparedEvent){System.out.println("ApplicationEnvironmentPrepared");}//context已经创建且没有refresh发送个事件if(eventinstanceofApplicationPreparedEvent){System.out.println("ApplicationPrepared");}//context已经refresh且application and command-line runners(如果有) 调用之前发送这个事件if(eventinstanceofApplicationStartedEvent){System.out.println("ApplicationStarted");}//application and command-line runners (如果有)执行完后发送这个事件,此时应用已经启动完毕if(eventinstanceofApplicationReadyEvent){// 此时,Spring 容器构建完成了,已经可以从中拿到 Bean 了ApplicationContext context=((ApplicationReadyEvent) event).getApplicationContext();SystemInitManager initService= context.getBean(SystemInitManager.class);
            initService.doSystemInit();}//应用启动失败后产生这个事件if(eventinstanceofApplicationFailedEvent){System.out.println("ApplicationFailed");}}}

由于我们要监听 Spring Boot 的启动过程, 那就需要在Spring Boot 程序刚启动时就执行, 那么就两种方式了

  1. main函数
@SpringBootApplicationpublicclassCrewWarApplication{publicstaticvoidmain(String[] args){newSpringApplicationBuilder().sources(CrewWarApplication.class).listeners(newCrewSystemEventListener()).run(args);}}
  1. spring.factories

在resources 下新建 META-INF 文件夹,在里面新建spring.factories文件, 在里面写入下面的代码段

众所周知, Spring Boot 程序一旦启动最先扫描的就是这玩意儿.

org.springframework.context.ApplicationListener=\
com.captain.springboot.war.event.CrewSystemEventListener

ps. 上面的示例代码为了验证在最后阶段 Spring 容器中的内容,用到了SystemInitManager 这玩意儿是我自己写的,在线面贴出来

publicinterfaceSystemInitManager{/**
     * 系统初始化
     */voiddoSystemInit();}@Component@Slf4jpublicclassCrewSystemInitManagerimplementsSystemInitManager{@OverridepublicvoiddoSystemInit(){
        log.info("系统初始化完成,你现在可以从 springContext 中获取 Bean");}}
  • 作者:平头小哥Crew
  • 原文链接:https://blog.csdn.net/qq_43705987/article/details/120103521
    更新时间:2022-07-21 12:25:26