欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > SpringEvent事件发布订阅Demo

SpringEvent事件发布订阅Demo

2024/10/24 13:20:56 来源:https://blog.csdn.net/qq_44036439/article/details/139376508  浏览:    关键词:SpringEvent事件发布订阅Demo

文章目录

  • 实现事件
  • 定义事件监听器
    • 方式一
    • 方式二
  • 定义事件发布者
    • 方式一
    • 方式二
  • 项目测试
  • 原理

本文参考:

基础用法:Spring Event事件发布&消费Demo - HumorChen99 - 博客园 (cnblogs.com)

比较全面的用法:Spring Event 事件发布/监听机制 详解并使用-CSDN博客

@EventListener 源码简单探究:SpringBoot中@EventListener注解的使用-CSDN博客

广播器与监听器初始化详细源码:深入理解Spring事件机制(一):广播器与监听器的初始化 - Createsequence - 博客园 (cnblogs.com)

事件如何广播推送的:[深入理解Spring事件机制(二):事件的推送通俗易懂]-腾讯云开发者社区-腾讯云 (tencent.com)

在看源码的过程中,看到大佬们通过 Spring Event 事件发布与订阅机制,都实现了比较优雅的代码,但是自己对于这方面的实践还是较少,因此这篇文章准备从 Demo 引入,了解相关的用法,后面再出文章进一步分析其中的原理。

实现Spring事件机制主要有4个类:

  1. ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
  2. ApplicationListener:事件监听器,用于接收事件处理时间。
  3. ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
  4. ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。

实现事件

自定义事件,继承 ApplicationEvent

package com.jxz;import lombok.Getter;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;/*** @Author jiangxuzhao* @Description 自定义事件,继承 ApplicationEvent* @Date 2024/5/31*/
@Getter
@ToString
public class JxzEvent extends ApplicationEvent {private String jxzTopic;private String jxzContent;public JxzEvent(String topic, String content) {super(topic);this.jxzTopic = topic;this.jxzContent = content;}
}

定义事件监听器

方式一

自定义事件监听器,实现 ApplicationListener 接口,监听 JxzEvent 事件,同时别忘了将 JxzEventListener 通过 @Component 注入给 Spring IOC 容器

package com.jxz;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** @Author jiangxuzhao* @Description 自定义事件监听器,实现 ApplicationListener 接口,监听 com.jxz.JxzEvent 事件* @Date 2024/5/31*/
@Slf4j
@Component
public class JxzEventListener implements ApplicationListener<JxzEvent> {@Overridepublic void onApplicationEvent(JxzEvent event) {log.info("topic is = {}", event.getJxzTopic());log.info("content is = {}", event.getJxzContent());}
}

方式二

通过 @EventListener 注解,实现监听事件的效果

package com.jxz;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** @Author jiangxuzhao* @Description* @Date 2024/6/1*/
@Component
@Slf4j
public class JxzEventListener2 {@EventListener(value = {JxzEvent.class}) // @EventListener 注解public void listener(JxzEvent event) {log.info("topic is = {}", event.getJxzTopic());log.info("content is = {}", event.getJxzContent());}
}

定义事件发布者

方式一

需要引入 ApplicationEventPublisher 事件发布者,然后构造 JxzEvent 事件发布事件

package com.jxz;import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author jiangxuzhao* @Description Controller 类方便测试* @Date 2024/5/31*/
@RestController
public class JxzController {/*** 需要引入 ApplicationEventPublisher 事件发布者*/@Resourceprivate ApplicationEventPublisher applicationEventPublisher;@GetMapping(path = "test/")public String test(String topic, String content) {// 通过传入参数构造 JxzEvent 事件JxzEvent jxzEvent = new JxzEvent(topic, content);// 发布事件applicationEventPublisher.publishEvent(jxzEvent);return jxzEvent.toString();}
}

方式二

Spring 中顶级的容器管理类 ApplicationContext

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver

extends 继承于 ApplicationEventPublisher,因此也可以通过注入父类的接口引入这个发布者

@Resource
private ApplicationContext applicationContext;@GetMapping(path = "/test2")
public String test2(String topic, String content) {// 通过传入参数构造 JxzEvent 事件JxzEvent jxzEvent = new JxzEvent(topic, content);// 发布事件applicationContext.publishEvent(jxzEvent);return jxzEvent.toString();
}

其实这两种方式都是通过接口的方式,自动装配了 AnnotationConfigServletWebServerApplicationContext 这个实现类。

项目测试

最后再给程序加一个启动类就可以测试了

package com.jxz;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author jiangxuzhao* @Description* @Date 2024/5/31*/
@SpringBootApplication
public class SpringEventApplication {public static void main(String[] args) {SpringApplication.run(SpringEventApplication.class, args);}
}

启动这个类并加上 VM Option = -Dserver.port=8071,本地端口 8071 接收请求

http://localhost:8071/test?topic="jxztopic"&content="明天放假"

控制台输出如下,表明事件成功发出,监听者成功接收并打印内容

2024-05-31 23:59:23.930  INFO 45974 --- [nio-8071-exec-4] com.jxz.JxzEventListener                 : topic is = "jxztopic"
2024-05-31 23:59:23.932  INFO 45974 --- [nio-8071-exec-4] com.jxz.JxzEventListener                 : content is = "明天放假"

原理

原理这块还是值得细细看源码的,这里姑且做个引子,起个提纲挈领的作用,需要有些 Spring 源码的底子,小弟我也只能管中窥豹。

本质上就是使用到了观察者模式,之前也写过一篇文章讲过 设计模式-从回调函数入手理解观察者模式_回调函数与观察者模式-CSDN博客

监听器的逻辑:

SpringApplication#run -> AbstractApplicationContext#createApplicationContext() -> AnnotationConfigServletWebServerApplicationContext -> new AnnotatedBeanDefinitionReader -> AnnotationConfigUtils.registerAnnotationConfigProcessors -> new RootBeanDefinition(EventListenerMethodProcessor.class) -> AbstractApplicationContext#finishBeanFactoryInitialization -> beanFactory.preInstantiateSingletons() -> smartSingleton.afterSingletonsInstantiated() -> EventListenerMethodProcessor#afterSingletonsInstantiated -> EventListenerMethodProcessor#processBean -> context.addApplicationListener -> applicationEventMulticaster.addApplicationListener(listener) -> AbstractApplicationEventMulticaster#addApplicationListener -> this.defaultRetriever.applicationListeners.add(listener)

由于 EventListenerMethodProcessor 实现了 SmartInitializingSingleton 接口,因此在 beanFactory 结束初始化结束之后会被调用,最终调用到其afterSingletonsInstantiated 方法,在 processBean 方法中,会找出所有被@EventListener 注解的方法,将它们初始化成 ApplicationListener 对象,并最终放入 AbstractApplicationEventMulticaster#ListenerRetriever#applicationListeners 集合中,同时放入 AbstractApplicationContext#applicationListeners 集合中,这里就是完成了监听者的注册。

发布者逻辑:

ApplicationEventPublisher#publishEvent -> AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) -> getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) -> SimpleApplicationEventMulticaster#multicastEvent

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 方法就会从通过 retrieveApplicationListeners 从前面注册的 defaultRetriever.applicationListeners 里面拿到所有的监听者,同时配置了一个本地缓存 retrieverCache 来提速。最终调用 invokeListener -> doInvokeListener -> listener.onApplicationEvent(event) 就是成功调用了这些监听者的 onApplicationEvent 方法。

由此可见,观察者模式是通过同步的方式实现的事件通知,而非消息队列那样依赖消息中心实现的异步通知方式。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com