導言
我們在日常工作中,總會遇到像用戶註冊完之後,需要給用戶發送一條短信來通知用戶的要求,由於發短信接口可能不穩定,但我們不希望短信的不穩定影響正常業務的處理,這時我們可以考慮使用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註解即可