Spring提供了事件监听,流程如下:
定义事件
本列中定义了日志记录事件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;
}
}
注册事件监听器
定义ApiEventListener监听器,需要继承ApplicationListener接口,并实现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调用事件,内容 {} ",event.getContent());
}
}
发布事件
@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()方法发布ApiEvent事件。
程序改进
- Spring提供了注解
@EventListener
简化监听器 - 异步执行事件
- 多个监听器监听同一个事件时,按顺序执行
- 使用自Annotation+AOP的方式,简化事件发布
@EventListener注解,定义监听器
@EventListener
标记在方法上,表示该方法是一个监听器。如果该方法只监听一个事件,可使用方法签名的方式监听事件
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api调用事件,内容 {} ",event.getContent());
}
如果被注解的方法需要处理多种类型的事件,如Api调用事件,Log记录事件等,需要使用Classes
属性。如果Classes指定了多个值,那么,被标记的方法不能有任何参数。
@EventListener({EventA.class, EventB.class})
public void ApiEventListener(ApiEvent event){
logger.info("收到事件");
}
这样一来,我们就不能处理事件带来的附加信息。所以,建议为每个事件单独定义一个处理器。
@Async注解,定义异步
默认情况,事件时同步的,及只有当监听器的处理方法执行完成后,才会执行剩下的步骤。对于耗时很长且不影响后续业务的方法(如:将事件记录到数据库中),可以使用异步的方式处理事件。
@Async
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api调用事件,内容 {} ",event.getContent());
}
使用 @Async
标记事件处理器为异步方法。
默认情况下,Spring没有开启Async,使用 @EnableAsync
注解使 @Async
有效。
@Order注解,定义监听器的执行顺序
可以使用 @Order(100)
注解来标记事件的监听执行顺序,异步的情况下只保证按顺序将监听器丢入进线程池。
@Async
@Order(100)
@EventListener
public void ApiEventListener(ApiEvent event){
logger.info("收到Api调用事件,内容 {} ",event.getContent());
}
@Async
@Order(101)
@EventListener
public void ApiEventListener2(ApiEvent event){
logger.info("收到Api调用事件,内容 {} ",event.getContent());
}
使用AOP简化事件发布
首先,我们自定义一个注解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 {
//执行方法
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;
}