Spring事件監(jiān)聽

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

image-20200708144655101

定義事件

本列中定義了日志記錄事件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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容