- 4.1. @EventListener
- 4.2. @TransactionEventListener
- 4.3. @EventListener + @Async
- 4.4. @TransactionEventListener + @Async
摘要: 原创出处 https://www.iocoder.cn/Fight/no-title/ 「芋道源码」欢迎转载,保留摘要,谢谢!
- [Redis 常见面试知识点小结](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect)
- [谈谈 Java 接口 Result 设计](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect)
- [小项目需要前后端分离吗?](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect)
- [阿里技术专家详解 DDD 系列](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect)
- [如何吃透一个 Java 项目?(附学习实践)](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect)
1. 领域事件
领域事件是 DDD 中重要的模式之一,主要用于模型或系统间的解耦,提高系统的可扩展性和可维护性。
1.1. 什么是领域事件
领域事件是领域驱动设计(Domain-Driven Design,简称DDD)中的一个重要概念,特指在领域模型中发生的有意义的事件,是对领域模型中的重要业务动作执行结果的抽象,如订单创建、支付完成等。
在DDD中,领域事件是一种用于传递信息的机制,它使得不同领域模型之间的通信变得更加简单和灵活。通过将事件分发给相关的订阅者,可以让不同的领域模型之间实现松耦合,从而更容易扩展和维护应用程序。
领域事件通常由领域对象主动触发并发布,而事件处理器则负责订阅事件并对事件进行处理。通过事件发布和订阅机制,可以在应用程序中实现高效的事件驱动架构,从而更好地支持复杂的业务逻辑和业务流程。
说起来有点抽象,简单举个例子:假设有一个电子商务系统,用户下单后需要生成订单并发送通知给相关人员。在领域模型中,可以定义一个 Order 领域对象,该对象可以包含多个属性,如订单号、下单时间、购买的商品信息、收货地址等等。当用户下单时,可以通过调用 Order 对象的方法来生成订单,同时也可以通过领域事件来发送通知。
具体来说,可以定义一个 OrderCreated 领域事件,用于表示订单创建完成的事件,该事件包含一些必要的属性,如订单号、下单时间、购买的商品信息、收货地址等等。当 Order 对象创建完成后,可以通过领域事件来触发发送通知的操作,比如发送邮件或短信通知相关人员。
1.2. 领域事件的应用场景
领域事件的应用创建众多,从图中可以看出:
领域事件可以:
- 保证聚合间的数据一致性。当一个聚合根上的操作引发了其他聚合根的变更时,将这些变更作为领域事件发布出去,其他聚合根可以订阅这些事件并更新自己的状态,从而实现最终一致性。
- 替换批量处理。可以作为任务的触发器,例如定时任务、异步任务,避免定时+扫描这类批量处理。
- 实现事件源模式。将所有的领域事件全部存储下来,可以用于恢复聚合的状态,实现事件源模式;也可以用于后续的审计和调试。
- 进行限界上下文集成。将事件从一个子域发布到另一个子域,使得这两个子域可以解耦,不用相互知道彼此的存在。
领域事件虽好,但仍需技术框架进行支持,其实 Spring 的 Event 机制就足以满足各类需求。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
2. Spring 对 Event 的支持
在 Spring 中,事件的处理可以通过三种方式来实现:
- 基于接口的事件处理:通过实现 ApplicationListener 接口并重写 onApplicationEvent 方法来处理事件。
- 基于注解的事件处理:通过在方法上添加 @EventListener 或 @TransactionEventListener 注解来处理事件,可以指定事件的类型以及监听的条件等。
- 基于异步事件处理:通过使用 @Async 注解来异步处理事件,可以提高应用程序的响应速度。
2.1. 基于接口的事件处理
由于与 Spring 存在强耦合,现在已经很少使用,可以直接跳过。
下面是一个基于接口的事件处理的示例代码:
@Component public class MyEventListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent event) { // 处理事件 System.out.println("Received event: " + event.getMessage()); } } public class MyEvent { private String message; public MyEvent(String message) { this.message = message; } public String getMessage() { return message; } } @Component public class MyEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishEvent(String message) { MyEvent event = new MyEvent(message); eventPublisher.publishEvent(event); } }
在这个示例中,MyEvent 是一个自定义的事件类,MyEventListener 是一个实现了 ApplicationListener 接口的监听器,用于处理 MyEvent 事件,MyEventPublisher 是用于发布事件的类。
当应用程序调用 MyEventPublisher 的 publishEvent 方法时,会触发一个 MyEvent 事件,MyEventListener 中的 onApplicationEvent 方法将被自动调用,从而处理这个事件。
2.2. 基于注解的事件处理
Spring 提供 @EventListener 和 @TransactionListener 两个注解以简化对事件的处理。
2.2.1. @EventListener
Spring 的 EventListener 监听器是一种相对于传统的事件监听方式更为简洁和灵活的事件机制。与传统的事件机制不同,EventListener 不需要显示地继承特定的事件接口,而是使用注解标识需要监听的事件类型,然后通过一个单独的监听器类处理所有类型的事件。
相比之下 EventListener 的优势主要有以下几点:
- 更加灵活:EventListener 不依赖于任何特定的事件接口,从而使得事件处理更加灵活,可以监听和处理任意类型的事件。
- 更加简洁:相比传统的事件监听方式,使用 EventListener 可以避免一系列繁琐的接口定义和实现,简化了代码结构,使得开发效率更高。
- 更加松耦合:EventListener 将事件发布方和事件处理方分离,遵循松耦合的设计原则,提高了代码的可维护性和扩展性。
- 更加可测试:由于 EventListener 可以监听和处理任意类型的事件,可以通过单元测试验证其功能是否正确,从而提高了测试的可靠性。
以下是一个简单的例子:
@Component public class MyEventListener{ @EventListener public void onApplicationEvent(MyEvent event) { // 处理事件 System.out.println("Received event: " + event.getMessage()); } } public class MyEvent { private String message; public MyEvent(String message) { this.message = message; } public String getMessage() { return message; } } @Component public class MyEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishEvent(String message) { MyEvent event = new MyEvent(message); eventPublisher.publishEvent(event); } }
相比基于接口的事件处理,EventListener 是一种更加简洁、灵活、松耦合、可测试的事件机制,能够有效地降低开发的复杂度,提高开发效率。
2.2.2. @TransactionEventListener
在 Spring 中,TransactionEventListner 和 EventListner 都是用于处理事件的接口。不同之处在于
- TransactionEventListner 是在事务提交后才会触发
- 而 EventListner 则是在事件发布后就会触发。
具体来说,在使用 Spring 的声明式事务时,可以在事务提交后触发某些事件。这就是 TransactionEventListner 的应用场景。而 EventListner 则不涉及事务,可以用于在事件发布后触发一些操作。
下面是一个简单的示例,演示了如何使用 TransactionEventListner 和 EventListner:
@Component public class MyEventListener { @EventListener public void handleMyEvent(MyEvent event) { // 处理 MyEvent } @TransactionalEventListener public void handleMyTransactionalEvent(MyTransactionalEvent event) { // 处理 MyTransactionalEvent } } @Service public class MyService { @Autowired private ApplicationEventPublisher eventPublisher; @Autowired private MyRepository myRepository; @Transactional public void doSomething() { // 做一些事情 MyEntity entity = myRepository.findById(1L); // 发布事件 eventPublisher.publishEvent(new MyEvent(this, entity)); // 发布事务事件 eventPublisher.publishEvent(new MyTransactionalEvent(this, entity)); } }
在这个例子中,MyEventListener 类定义了两个方法,handleMyEvent 和 handleMyTransactionalEvent,分别处理 MyEvent 和 MyTransactionalEvent 事件。其中,handleMyTransactionalEvent 方法用 @TransactionalEventListener 注解标记,表示它只会在事务提交后触发。
MyService 类中的 doSomething 方法使用 ApplicationEventPublisher 来发布事件。注意,它发布了两种不同类型的事件:MyEvent 和 MyTransactionalEvent。这两个事件会分别触发 MyEventListener 中的对应方法。
总的来说,Spring 的事件机制非常灵活,可以方便地扩展应用程序的功能。TransactionEventListner 和 EventListner 这两个接口的应用场景有所不同,可以根据实际需求选择使用。