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註解即可


分享到:


相關文章: