【SpringBoot】使用SpringEvent事件通知做异步消息处理

前言

我们在项目中经常会碰见类似诸如用户支付完订单后,通知用户支付成功,并且商品库存对应减少这样的需求,也就是做完主业务流程之后,希望异步执行一些其他的操作。接下来我们来探究解决这个问题的几种方案来引出SpringEvent,这里的方案就先不讨论MQ队列

实现

针对上面的业务需求,我们大概会做如下的代码。

  • 同步情况
@Service
public class OrderService {

    public void pay() {
        System.out.println("支付成功逻辑...");
        System.out.println("商品库存减少...");
        System.out.println("短信通知...");
    }
}
  • 异步情况
@Service
public class OrderService {

    public void pay() {
        System.out.println("支付成功逻辑...");
        //简单异步,也可以用线程池,效果一样能说明问题
        new Thread(()->{
            System.out.println("商品库存减少...");
            System.out.println("短信通知...");
        }).start();
    }
}

假设商品库存减少和短信通知的代码超过了几十行,可想而知,代码复杂度就上去了。如果说支付成功逻辑之后,只走商品库存减少或者短信通知两个业务还好,万一以后再加其他代码呢,那就跟垒积木一样,对于后期维护极其不友好,全部逻辑都冗余在一起,违背了单一职责原则

这时候我们极其需要一种能高度解耦的一种方式,将这些代码抽离出去,各自维护,有比较好的边界,接下来即将引出SpringEvent

方案1:封装成方法

既然说代码放一起比较复杂,那我们就封装成方法,用调用方法的方式来执行,这样以后开发人员去找对应的方法去维护对应的业务就可以了。

@Service
public class OrderService {
    @Autowired
    GoodsService goodsService;
    @Autowired
    SMSService smsService;

    public void pay() {
        System.out.println("支付成功逻辑...");
        //简单异步,也可以用线程池,效果一样能说明问题
        new Thread(()->{
        	//减少库存逻辑
            goodsService.reduceStock();
            //短信通知逻辑
            smsService.smsNotify();
        }).start();
    }
}

再将直接new Thread方式优化,使用EnableAsync注解的话,代码如下

@Configuration
@EnableAsync
public class Config{
    
}

@Service
public class OrderService {
    @Autowired
    GoodsService goodsService;
    @Autowired
    SMSService smsService;

    public void pay() {
        System.out.println("支付成功逻辑...");
        //减少库存逻辑
        goodsService.reduceStock();
        //短信通知逻辑
        smsService.smsNotify();
    }
}

@Service
public class GoodsService{
    @Async
    public void reduceStock(){
        //减少库存逻辑....
    }
}

public class SMSService{
    @Async
    public void  smsNotify(){
        //短信通知逻辑...
    }
}
方案2:SpringEvent

接下来就是我们的SpringEvent闪亮登场,Event顾名思义就是Spring事件监听机制。

  • 添加依赖
 <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

不过如果你是SpringBoot项目,基本不用引入了,肯定都有这个依赖的。

  • 创建事件对象

    我们这里有两个事件,分别为减少库存短信通知,所以接下来我们要创建两个事件对象GoodsReduceEventSMSNotifyEvent,他们都要继承ApplicationEvent

    /**
     * 库存减少事件
     */
    public class GoodsReduceEvent extends ApplicationEvent {
    
        private String message;
    
        public String getMessage() {
            return message;
        }
    
        public GoodsReduceEvent(Object source, String message){
            super(source);
            
        }
    }
    
    /**
    *@Description   短信发送事件
    *@Author        wengzhongjie
    *@Date          2022/7/20 10:15
    *@Version
    */
    public class SMSNotifyEvent extends ApplicationEvent {
    
        private String message;
    
        public String getMessage() {
            return message;
        }
    
        public SMSNotifyEvent(Object source, String message){
            super(source);
            this.message = message;
        }
    }
    
  • 创建监听器

    既然事件对象有两个,那么监听器我们也得创建两个GoodsReduceListenerSMSMessageNotifyListener。这里要写一个对应的业务方法,在方法上加上@EventListener注解,然后在方法参数上使用对应的事件对象。

    /**
    *@Description   监听库存减少事件的监听器
    *@Author        wengzhongjie
    *@Date          2022/7/20 14:22
    *@Version
    */
    @Component
    public class GoodsReduceListener {
    
        @EventListener
        public void goodsReduce(GoodsReduceEvent goodsReduceEvent) {
            System.out.println(Thread.currentThread().getName()+"------"+goodsReduceEvent.getMessage());
        }
    }
    
    /**
    *@Description   监听发送短信事件的监听器
    *@Author        wengzhongjie
    *@Date          2022/7/20 14:24
    *@Version       
    */
    @Component
    public class SMSMessageNotifyListener {
    
        /**
         * 发送短信
         */
        @EventListener
        public void sendSMS(SMSNotifyEvent event) throws InterruptedException {
            System.out.println(Thread.currentThread().getName()+"------"+event.getMessage());
        }
    }
    
  • 发布事件

    接下来就是改造刚才的下单逻辑代码,使用ApplicationEventPublisher来统一发布事件。

    @Service
    public class OrderService {
        @Autowired
        ApplicationEventPublisher applicationEventPublisher;
    
        public void pay() {
            System.out.println("支付成功逻辑...");
            //库存减少
            applicationEventPublisher.publishEvent(new GoodsReduceEvent(this,"减少库存"));
            //短信通知
            applicationEventPublisher.publishEvent(new SMSNotifyEvent(this,"发送短信"));
        }
    }
    
  • 效果

在这里插入图片描述

  • 使用异步

    上面演示的其实是同步情况,如果想异步的话,还是可以使用@EnableAsync注解。

    @Configuration
    @EnableAsync
    public class Config{
        
    }
    

    在对应的监听器上加@Async注解即可。

      /**
      *@Description   监听发送短信事件的监听器
      *@Author        wengzhongjie
      *@Date          2022/7/20 14:24
      *@Version       
      */
      @Component
      public class SMSMessageNotifyListener {
      
          /**
           * 发送短信
           */
          @Async
          @EventListener
          public void sendSMS(SMSNotifyEvent event) throws InterruptedException {
              System.out.println(Thread.currentThread().getName()+"------"+event.getMessage());
          }
      }
    

至此,两种方案都写出来了,大家可以比较一下哪种比较好,楼主还是比较喜欢使用SpringEvent,虽然说可能代码会多那么一点,但是解耦性和语义性上来说还是比较好的。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot中,可以使用事件机制来实现异步事件处理异步事件可以在应用程序中的不同组件之间进行通信,以实现解耦和并发处理。 要使用Spring Boot的事件机制,首先需要定义一个事件类,该类表示要发布的事件事件类可以是普通的POJO类,通常包含一些必要的属性和方法。 然后,需要定义一个事件监听器类,该类用于处理特定类型的事件。监听器类需要实现`ApplicationListener`接口,并通过泛型指定要监听的事件类型。在监听器类中,可以定义一个或多个处理事件的方法。 接下来,需要在应用程序中触发事件。可以通过使用`ApplicationEventPublisher`接口中的`publishEvent()`方法来发布事件。该接口可以通过自动装配的方式注入到需要触发事件的组件中。 最后,当触发事件时,监听器类中相应的方法将被调用来处理事件。如果希望异步处理事件,可以在监听器类中使用`@Async`注解来标记处理方法,以便在单独的线程中执行。 以下是一个简单的示例代码,演示了如何在Spring Boot中实现异步事件处理: ```java // 定义事件类 public class MyEvent extends ApplicationEvent { private String message; public MyEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } // 定义事件监听器类 @Component public class MyEventListener implements ApplicationListener<MyEvent> { @Async @Override public void onApplicationEvent(MyEvent event) { System.out.println("Received event: " + event.getMessage()); // 处理事件逻辑 } } // 触发事件的组件 @Component public class EventTriggerComponent { @Autowired private ApplicationEventPublisher eventPublisher; public void triggerEvent(String message) { MyEvent event = new MyEvent(this, message); eventPublisher.publishEvent(event); } } ``` 在上述示例中,`MyEvent`表示自定义的事件类,`MyEventListener`是事件监听器类,`EventTriggerComponent`是触发事件的组件。 当调用`EventTriggerComponent`的`triggerEvent()`方法时,会发布一个`MyEvent`事件,然后`MyEventListener`中的`onApplicationEvent()`方法将被异步调用来处理事件。 需要注意的是,为了让Spring Boot支持异步事件处理,还需要在配置类中添加`@EnableAsync`注解。 这就是使用Spring Boot实现异步事件处理的基本步骤。通过事件机制,可以方便地在应用程序中实现解耦和并发处理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰肥啊

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值