Spring Boot Event 观察者模式,轻松实现业务解耦!

爱被打了一巴掌 2023-09-28 15:07 58阅读 0赞

实际业务开发过程中,业务逻辑可能非常复杂,核心业务 + N个子业务。如果都放到一块儿去做,代码可能会很长,耦合度不断攀升,维护起来也麻烦,甚至头疼。还有一些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

MQ 确实可以解决这个问题,但 MQ 重啊,非必要不提升架构复杂度。

针对这些问题,我们了解一下 Spring Event。

Spring Event 同步使用

Spring Event(Application Event)其实就是一个观察者设计模式,一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

Spring Event 用来解耦业务真的贼好用!

1. 自定义事件

定义事件,继承 ApplicationEvent 的类成为一个事件类

  1. /**
  2. * @author Strive
  3. * @date 2022/4/22 18:00
  4. * @description
  5. */
  6. @Data
  7. @ToString
  8. public class OrderProductEvent extends ApplicationEvent {
  9. /** 该类型事件携带的信息 */
  10. private String orderId;
  11. public OrderProductEvent(Object source, String orderId) {
  12. super(source);
  13. this.orderId = orderId;
  14. }
  15. }

2. 定义监听器

监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解;关注公众号:“码猿技术专栏”,回复关键词:“3333”,获取阿里内部《Spring Cloud Alibaba实战》手册

  1. /**
  2. * 实现 ApplicationListener 接口,并指定监听的事件类型
  3. *
  4. * @author Strive
  5. * @date 2022/4/24 09:09
  6. * @description
  7. */
  8. @Slf4j
  9. @Component
  10. public class OrderProductListener implements ApplicationListener<OrderProductEvent> {
  11. /** 使用 onApplicationEvent 方法对消息进行接收处理 */
  12. @SneakyThrows
  13. @Override
  14. public void onApplicationEvent(OrderProductEvent event) {
  15. String orderId = event.getOrderId();
  16. long start = System.currentTimeMillis();
  17. Thread.sleep(2000);
  18. long end = System.currentTimeMillis();
  19. log.info("{}:校验订单商品价格耗时:({})毫秒", orderId, (end - start));
  20. }
  21. }

3. 定义发布者

发布事件,通过 ApplicationEventPublisher 发布事件;

推荐个人知识总结网站:www.java-family.cn

  1. /**
  2. * @author Strive
  3. * @date 2022/4/24 09:25
  4. * @description
  5. */
  6. @Slf4j
  7. @Service
  8. @RequiredArgsConstructor
  9. public class OrderService {
  10. /** 注入ApplicationContext用来发布事件 */
  11. private final ApplicationContext applicationContext;
  12. /**
  13. * 下单
  14. *
  15. * @param orderId 订单ID
  16. */
  17. public String buyOrder(String orderId) {
  18. long start = System.currentTimeMillis();
  19. // 1.查询订单详情
  20. // 2.检验订单价格 (同步处理)
  21. applicationContext.publishEvent(new OrderProductEvent(this, orderId));
  22. // 3.短信通知(异步处理)
  23. long end = System.currentTimeMillis();
  24. log.info("任务全部完成,总耗时:({})毫秒", end - start);
  25. return "购买成功";
  26. }
  27. }

4.单测执行

  1. @SpringBootTest
  2. public class OrderServiceTest {
  3. @Autowired private OrderService orderService;
  4. @Test
  5. public void buyOrderTest() {
  6. orderService.buyOrder("732171109");
  7. }
  8. }

执行结果如下:

  1. 2022-04-24 10:13:17.535 INFO 44272 --- [ main] c.c.m.e.listener.OrderProductListener : 732171109:校验订单商品价格耗时:(2008)毫秒
  2. 2022-04-24 10:13:17.536 INFO 44272 --- [ main] c.c.mingyue.event.service.OrderService : 任务全部完成,总耗时:(2009)毫秒

Spring Event 异步使用

有些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

1. 自定义事件

  1. @Data
  2. @AllArgsConstructor
  3. public class MsgEvent {
  4. /** 该类型事件携带的信息 */
  5. public String orderId;
  6. }

2. 定义监听器

推荐使用 @EventListener 注解

  1. @Slf4j
  2. @Component
  3. public class MsgListener {
  4. @SneakyThrows
  5. @EventListener(MsgEvent.class)
  6. public void sendMsg(MsgEvent event) {
  7. String orderId = event.getOrderId();
  8. long start = System.currentTimeMillis();
  9. log.info("开发发送短信");
  10. log.info("开发发送邮件");
  11. Thread.sleep(4000);
  12. long end = System.currentTimeMillis();
  13. log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));
  14. }
  15. }

3. 定义发布者

  1. /**
  2. * 下单
  3. * @param orderId 订单ID
  4. */
  5. public String buyOrder(String orderId) {
  6. long start = System.currentTimeMillis();
  7. // 1.查询订单详情
  8. // 2.检验订单价格 (同步处理)
  9. applicationContext.publishEvent(new OrderProductEvent(this, orderId));
  10. // 3.短信通知(异步处理)
  11. applicationContext.publishEvent(new MsgEvent(orderId));
  12. long end = System.currentTimeMillis();
  13. log.info("任务全部完成,总耗时:({})毫秒", end - start);
  14. return "购买成功";
  15. }

4. 单测执行(同步)

  1. @Test
  2. public void buyOrderTest() {
  3. orderService.buyOrder("732171109");
  4. }

执行结果如下:

  1. 2022-04-24 10:24:13.905 INFO 54848 --- [ main] c.c.m.e.listener.OrderProductListener : 732171109:校验订单商品价格耗时:(2004)毫秒
  2. 2022-04-24 10:24:13.906 INFO 54848 --- [ main] c.c.mingyue.event.listener.MsgListener : 开发发送短信
  3. 2022-04-24 10:24:13.907 INFO 54848 --- [ main] c.c.mingyue.event.listener.MsgListener : 开发发送邮件
  4. 2022-04-24 10:24:17.908 INFO 54848 --- [ main] c.c.mingyue.event.listener.MsgListener : 732171109:发送短信、邮件耗时:(4002)毫秒
  5. 2022-04-24 10:24:17.908 INFO 54848 --- [ main] c.c.mingyue.event.service.OrderService : 任务全部完成,总耗时:(6008)毫秒

5.开启异步

启动类增加 @EnableAsync 注解

  1. @EnableAsync
  2. @SpringBootApplication
  3. public class MingYueSpringbootEventApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(MingYueSpringbootEventApplication.class, args);
  6. }
  7. }

Listener 类需要开启异步的方法增加 @Async 注解

  1. @Async
  2. @SneakyThrows
  3. @EventListener(MsgEvent.class)
  4. public void sendMsg(MsgEvent event) {
  5. String orderId = event.getOrderId();
  6. long start = System.currentTimeMillis();
  7. log.info("开发发送短信");
  8. log.info("开发发送邮件");
  9. Thread.sleep(4000);
  10. long end = System.currentTimeMillis();
  11. log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));
  12. }

6.单测执行(异步)

发送短信的线程显示 task-1,主线程结束后(总耗时:(2017)毫秒)控制台停止打印了

  1. 2022-04-24 10:30:59.002 INFO 59448 --- [ main] c.c.m.e.listener.OrderProductListener : 732171109:校验订单商品价格耗时:(2009)毫秒
  2. 2022-04-24 10:30:59.009 INFO 59448 --- [ main] c.c.mingyue.event.service.OrderService : 任务全部完成,总耗时:(2017)毫秒
  3. 2022-04-24 10:30:59.028 INFO 59448 --- [ task-1] c.c.mingyue.event.listener.MsgListener : 开发发送短信
  4. 2022-04-24 10:30:59.028 INFO 59448 --- [ task-1] c.c.mingyue.event.listener.MsgListener : 开发发送邮件

" class="reference-link">bcf45fd9f3e74cc891ce5dede72dac0b.jpeg

发表评论

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

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

相关阅读

    相关 Java-观察模式业务实战

    Java-观察者模式业务实战 引言 观察者模式是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化