Spring提供了事件監(jiān)聽,流程如下:

定義事件
本列中定義了日志記錄事件ApiEvent,是ApplicationEvent的子類。
//定義日志記錄事件
public class ApiEvent extends ApplicationEvent {
private String content;
public ApiEvent(Object source, String content) {
super(source);
this.content = content;
}
public String getContent() {
return content;
}
}
注冊事件監(jiān)聽器
定義ApiEventListener監(jiān)聽器,需要繼承ApplicationListener接口,并實現(xiàn)onApplicationEvent方法,通過@Component注入到Context中。
@Component
public class ApiEventListener implements ApplicationListener<LogEvent> {
private static final Logger logger = LoggerFactory.getLogger(ApiEventListener.class);
@Override
public void onApplicationEvent(ApiEvent event) {
logger.info("收到Api調(diào)用事件,內(nèi)容 {} ",event.getContent());
}
}
發(fā)布事件
@PostMapping("/login")
public UserVO login(String username,String password){
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext();
context.publishEvent(new ApiEvent(this,"用戶登錄"));
UserVO dto = service.login(username,password);
return dto;
}
在接口中通過WebApplicationContextUtils獲取WebApplicationContext,通過publishEvent()方法發(fā)布ApiEvent事件。
程序改進
- Spring提供了注解
@EventListener簡化監(jiān)聽器 - 異步執(zhí)行事件
- 多個監(jiān)聽器監(jiān)聽同一個事件時,按順序執(zhí)行
- 使用自Annotation+AOP的方式,簡化事件發(fā)布
@EventListener注解,定義監(jiān)聽器
@EventListener標(biāo)記在方法上,表示該方法是一個監(jiān)聽器。如果該方法只監(jiān)聽一個事件,可使用方法簽名的方式監(jiān)聽事件
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api調(diào)用事件,內(nèi)容 {} ",event.getContent());
}
如果被注解的方法需要處理多種類型的事件,如Api調(diào)用事件,Log記錄事件等,需要使用Classes屬性。如果Classes指定了多個值,那么,被標(biāo)記的方法不能有任何參數(shù)。
@EventListener({EventA.class, EventB.class})
public void ApiEventListener(ApiEvent event){
logger.info("收到事件");
}
這樣一來,我們就不能處理事件帶來的附加信息。所以,建議為每個事件單獨定義一個處理器。
@Async注解,定義異步
默認(rèn)情況,事件時同步的,及只有當(dāng)監(jiān)聽器的處理方法執(zhí)行完成后,才會執(zhí)行剩下的步驟。對于耗時很長且不影響后續(xù)業(yè)務(wù)的方法(如:將事件記錄到數(shù)據(jù)庫中),可以使用異步的方式處理事件。
@Async
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api調(diào)用事件,內(nèi)容 {} ",event.getContent());
}
使用 @Async 標(biāo)記事件處理器為異步方法。
默認(rèn)情況下,Spring沒有開啟Async,使用 @EnableAsync 注解使 @Async 有效。
@Order注解,定義監(jiān)聽器的執(zhí)行順序
可以使用 @Order(100) 注解來標(biāo)記事件的監(jiān)聽執(zhí)行順序,異步的情況下只保證按順序?qū)⒈O(jiān)聽器丟入進線程池。
@Async
@Order(100)
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api調(diào)用事件,內(nèi)容 {} ",event.getContent());
}
@Async
@Order(101)
@EventListener
public void ApiEventListener2(ApiEvent event){
logger.info("收到Api調(diào)用事件,內(nèi)容 {} ",event.getContent());
}
使用AOP簡化事件發(fā)布
首先,我們自定義一個注解ApiLog
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
/**
* 描述
*
* @return {String}
*/
String value() default "";
}
再定義一個切面
public class ApiLogAspect {
@Around("@annotation(apiLog)")
public Object around(ProceedingJoinPoint point, ApiLog apiLog) throws Throwable {
//執(zhí)行方法
Object result = point.proceed();
//記錄日志
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext();
context.publishEvent(new ApiEvent(this,apiLog.value()));
return result;
}
}
改造接口方法
@ApiLog("用戶登錄")
@PostMapping("/login")
public UserVO login(String username,String password){
UserVO dto = service.login(username,password);
return dto;
}