Spring如何优雅地发送异步通知

导言

我们在日常工作中,总会遇到像用户注册完之后,需要给用户发送一条短信来通知用户的要求,由于发短信接口可能不稳定,但我们不希望短信的不稳定影响正常业务的处理,这时我们可以考虑使用Spring的事件机制注解@EventListener和@Async完成异步发送消息。注@EventListener是Spring4.2及以后才有的注解

Spring事件机制原理

Spring如何优雅地发送异步通知

首先我们可以从上图中可以看出,Spring的事件机制主要包含三个部分

  1. ApplicationEventPublisher主要负责发布事件
  2. ApplicationListener主要是负责事件的监听
  3. ApplicationEvent是事件的传输载体

Spring4.2以前版本的实现

1.事件参数需要继承ApplicationEvent

2.实现一个ApplicationListener的onApplicationEvent来处理事件


3.ApplicationEventPublisher的publishEvent方法来发送事件

代码示例如下:

1.定义一个事件参数载体类

@Data
public class ExampleEvent extends ApplicationEvent{
 private UserDto userDto;
 
 public ExampleEvent(Object source) {
 super(source);
 }
 public ExampleEvent(Object source,UserDto userDto){
 super(source);
 this.userDto = userDto;
 }
}

2.实现一个ApplicationListener的监听类

@Component
public class ExampleListener implements ApplicationListener{
 @Override
 public void onApplicationEvent(ExampleEvent event) {
 UserDto userDto = event.getUserDto();
 System.out.println(new Gson().toJson(userDto));
 }
}

3.在业务流程中发布事件

@Service
public class UserService {
 @Autowired
 private ApplicationEventPublisher publisher;
 
 public void addUser(UserDto userDto){
 //省略代码 
 publisher.publishEvent(new ExampleEvent(this,userDto));
 }
}

从上面的步骤中我们发现,Spring4.2以前的事件版本,如果想实现异步事件还需要额外的配置,这里暂时就不赘述了,接下来我们来了解一下Spring4.2及以后的实现方式

@EventListener和@Async完成异步发送消息

1.事件的发送还是使用ApplicationEventPublisher与之前的版本一致

@Service
public class UserService {
 @Autowired
 private ApplicationEventPublisher publisher;
 
 public void addUser(UserDto userDto){
 //省略业务代码
 publisher.publishEvent(userDto);
 }
}

2.实现事件异步监听时,必须使用@EnableAsync来开启异步

@Service
@EnableAsync
public class SMSService {
 @EventListener
 @Async
 public void sendMessage(UserDto userDto){
 System.out.println("发送短信{}",new Gson().toJson(userDto));
 }
}

3.效果展示,可以清晰地看出来发送消息的线程与业务处于不同线程之中

Spring如何优雅地发送异步通知

我们可以从中发现事件传输载体只需要普通的DTO类就可以了,无须继承ApplicationEvent,事件的监听器也无须实现ApplicationListener接口,取而代之是@EventListener,spring4.2以后的事件使用方式可以说是非常的便捷。但有时我们需要在事务提交后,才能去通知用户,避免事务失败还去通知用户的窘境,然而@
TransactionalEventListener恰恰可以解决我们的问题,它可以让事务和事件绑定在一起,使用方法如下:

@TransactionalEventListener

@Service
public class EmailService {
 @TransactionalEventListener(fallbackExecution=true,phase= TransactionPhase.AFTER_COMMIT)
 public void sendEmail(UserDto userDto){
 log.info("发送内容:{}",new Gson().toJson(userDto));
 }
}

phase还可以指定其他的,比如事务提交前(BEFORE_COMMIT),事务回滚后(AFTER_ROLLBACK)等其他场景

效果演示:

Spring如何优雅地发送异步通知

如果想异步,首先需要确保@EnableAsync在项目中已经配置,然后在方法上加@Async注解即可


分享到:


相關文章: