Spring如何優雅地發送異步通知

導言

我們在日常工作中,總會遇到像用戶註冊完之後,需要給用戶發送一條短信來通知用戶的要求,由於發短信接口可能不穩定,但我們不希望短信的不穩定影響正常業務的處理,這時我們可以考慮使用Spring的事件機制註解@EventListener和@Async完成異步發送消息。注@EventListener是Spring4.2及以後才有的註解

Spring事件機制原理

首先我們可以從上圖中可以看出,Spring的事件機制主要包含三個部分

ApplicationEventPublisher主要負責發佈事件ApplicationListener主要是負責事件的監聽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.效果展示,可以清晰地看出來發送消息的線程與業務處於不同線程之中

我們可以從中發現事件傳輸載體只需要普通的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)等其他場景

效果演示:

如果想異步,首先需要確保@EnableAsync在項目中已經配置,然後在方法上加@Async註解即可