Spring事件机制

淩亂°似流年 2023-01-01 01:45 301阅读 0赞
1.背景

事件驱动的一个常见形式便是发布-订阅模式。在跨进程的通信间,我们通常采用引入 MQ (消息队列) 来实现消息的发布和订阅。目前主流应用的架构中,均采用消息的发布-订阅模式来进行大型分布式系统的解耦。使得数据生产方和使用方分离,同时 MQ 还可起到削峰等作用。

同一进程内很多时候也需要这种事件驱动机制来进行逻辑解耦。 试想如下场景: 现在系统中需要针对用户操作的行为进行记录,记录按照业务需求需存入缓存、MQ两处。如果此处不进行解耦直接在原有程序添加,代码如下:

  1. public void xxService() {
  2. //原有业务代码
  3. recordCache();
  4. recordMQ();
  5. }

解析:
在上面代码示例中,我们模拟了直接在原有代码上增加记录代码后的示例。这段代码虽然能够满足需求,但是在扩展性上很差。可能会出现的问题大概有以下两个方面: 1. 如果我们未来需在同项目不同的业务代码中增加新的记录时,仍需要添加同样的代码,且 recordCache() 和 recordMQ() 若想进行复用,必然要将其封装到一个新的类中,但是在这种场景下封装一个新的类是没有语义的。 2. 如果未来该记录需要存储到第三个地方,则需要改动每个代码,实现新方式的增加,这显然是不够理想的。 在这种情况下,使用进程内的事件发布-订阅机制便可以大大增加该场景下代码的复用性和可读性。

2.使用

事件机制主要由三个部分组成:事件源,事件对象,监听器,具体描述如下: - 事件源:事件发生的起源 - 事件对象:事件实体,事件对象会持有一个事件源 - 监听器:监听事件对象,对事件对象进行处理。

(1)Java 提供的事件机制
Java 提供了事件相关的接口定义,具体包含以下两个: - EventObject: 事件对象,自定义事件对象需要继承该类 - EventListener: 事件监听器接口 由于事件源 Source 不需要实现任何接口,所以 Java 中没给出相应的定义。
展示一个简单的利用 Java 原生实现事件模型的例子:

  1. public class EventTest {
  2. public static void main(String[] args) {
  3. EventSource eventSource = new EventSource();
  4. TestEventListener listener = new TestEventListener();
  5. eventSource.addListener(listener);
  6. eventSource.publishEvent(new TestEvent(eventSource, "一个事件"));
  7. }
  8. }
  9. /** * 事件对象 */
  10. class TestEvent extends EventObject {
  11. private String msg;
  12. /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @param msg 附加消息 * @throws IllegalArgumentException if source is null. */
  13. public TestEvent(Object source, String msg) {
  14. super(source);
  15. this.msg = msg;
  16. }
  17. public String getMsg() {
  18. return msg;
  19. }
  20. }
  21. /** * 事件监听者,按照 Java 规范应实现 EventListener 接口 */
  22. class TestEventListener implements EventListener {
  23. public void handleEvent (TestEvent event) {
  24. System.out.println("TestEvent, msg is:" + event.getMsg());
  25. }
  26. }
  27. /** * 事件源 */
  28. class EventSource {
  29. private Set<EventListener> listenerSet = new HashSet<>();
  30. public void addListener(EventListener listener) {
  31. listenerSet.add(listener);
  32. }
  33. public void publishEvent(TestEvent event) {
  34. for (EventListener eventListener : listenerSet) {
  35. ((TestEventListener) eventListener).handleEvent(event);
  36. }
  37. }
  38. }

(2)Spring 事件使用
Spring 提供了事件相关的类和接口,在 Spring 项目中可以通过实现提供的接口,来实现事件的发布-订阅。Spring 的事件机制以 Java 的事件机制作为基础,按需进行了扩展。 Spring 中与事件相关的定义如下: - ApplicationEvent:继承 EventObject 类,事件源应继承该类; - ApplicationEventListener:事件监听者,该类接收一个泛型,供 ApplicationEventPublisher 在发布事件时选择 EventListener; - ApplicationEventPublisher:封装发布事件的方法,通知所有在 Spring 中注册的该事件的监听者进行处理; - ApplicationEventPublisherAware:Spring 提供的 Aware 接口之一,实现该接口的 Bean 可以获取 ApplicationEventPublisher 并进行发布事件。

展示一个简单利用 Spring 事件机制进行事件发布-订阅的例子:

  1. /** * 事件源 */
  2. @RestController
  3. @RequestMapping("/event")
  4. public class EventTestController implements ApplicationEventPublisherAware {
  5. private static final Logger logger = LoggerFactory.getLogger(EventTestController.class);
  6. private ApplicationEventPublisher applicationEventPublisher;
  7. @RequestMapping(value = "/publish", method = RequestMethod.GET)
  8. public void publishEvent(String msg) throws Exception {
  9. logger.info("msg is: {}", msg);
  10. //Spring进行事件的通知, 无需自行处理
  11. applicationEventPublisher.publishEvent(new TestEvent(this, msg));
  12. }
  13. @Override
  14. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
  15. this.applicationEventPublisher = applicationEventPublisher;
  16. }
  17. }
  18. /** * 事件对象 */
  19. public class TestEvent extends ApplicationEvent {
  20. private String msg;
  21. /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) */
  22. public TestEvent(Object source, String msg) {
  23. super(source);
  24. this.msg = msg;
  25. }
  26. public String getMsg() {
  27. return msg;
  28. }
  29. }
  30. /** * 事件监听者 * 事件监听者实现ApplicationListener<E extends ApplicationEvent>, 交由 Spring 进行管理,无需自己进行监听器的注册与通知过程 */
  31. @Component
  32. public class TestEventListener implements ApplicationListener<TestEvent> {
  33. private static final Logger logger = LoggerFactory.getLogger(TestEventListener.class);
  34. @Override
  35. public void onApplicationEvent(TestEvent event) {
  36. logger.info("publish event, msg is: {}", event.getMsg());
  37. }
  38. }

解析:
在 Spring 框架使用事件与在 Java 中使用时间机制其实并没有什么不同,均由 事件源、事件对象以及事件监听者组成。与 Java 原生提供的事件机制不同的是,Spring 中提供了 ApplicationEvent 类作为基类,开发者可以以此为基础定义自己的自定义事件。 在 Spring 中,继承自 ApplicationEvent 的事件对象的监听者,可以由 Spring 容器进行管理,并在发布时通过 ApplicationEventPublisher 进行发布。这就避免了我们自己实现监听者的注册和通知过程,免去了很多繁杂的过程,使得更专心于业务本身。

总结

本文主要介绍了进程内如何通过发布-订阅模式进行解耦,针对于 Spring 中的事件框架,还应有很多更深层次的挖掘,包括 ApplicationEventPublisher 的原理,以及事件处理中的一些多线程问题。这个放在日后与各位一起探讨。

发表评论

表情:
评论列表 (有 0 条评论,301人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring事件监听机制

    前言 Spring中的事件机制其实就是设计模式中的观察者模式,主要由以下角色构成: 1. 事件 2. 事件监听器(监听并处理事件) 3. 事件发布者(发布事件...

    相关 Spring事件机制

    1.背景 事件驱动的一个常见形式便是发布-订阅模式。在跨进程的通信间,我们通常采用引入 MQ (消息队列) 来实现消息的发布和订阅。目前主流应用的架构中,均采用消息的发布

    相关 spring事件广播机制

    打个比方,当我们和其他公司进行业务接口对接时,我们这边需要保存或者修改某些数据,但是保存和修改这些数据对整个流程没有太大的影响,这个时候我们应该怎么做呢?是的,我们要考虑异步去

    相关 spring事件机制

    事件驱动模型简介 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点: 1. 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目

    相关 spring事件机制

    事件驱动模型简介 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点: 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标

    相关 浅谈Spring事件机制

    一、同步事件和异步事件 同步事件:在一个线程里,按顺序执行业务,做完一件事再去做下一件事。 异步事件:在一个线程里,做一个事的同事,可以另起一个新的线程执行另一件事,这