iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Spring注解 @EventListener 的介绍与使用示例以及异常处理
  • 304
分享到

Spring注解 @EventListener 的介绍与使用示例以及异常处理

springjavaspringboot 2023-09-04 07:09:47 304人浏览 泡泡鱼
摘要

文章目录 基本概念用途事件处理条件处理器返回值异常处理异步监听器监听器排序源码 使用示例单一事件监听器使用classes实现多事件监听器使用condition筛选监听的事件有返回值的监听

文章目录

基本概念

用途

将一个方法标记为监听器,用于监听应用程序事件,事件可以是 ApplicationEvent实例,也可以是其他任意的对象。

如果一个监听器(被标注的方法)只支持单一的事件类型,那么该方法可以声明一个唯一的参数用来反映要监听的事件类型。

如果一个监听器(被标注的方法)支持多种事件类型,那么需要使用注解的classes属性指定一个或多个支持的事件类型。

事件处理条件

可以通过 condition 属性指定一个SpEL表达式,如果返回 "true", "on", "yes", or "1" 中的任意一个,则事件会被处理,否则不会。

处理器

@EventListener 注解的处理是通过内部的EventListenerMethodProcessor Bean进行的,当使用Java配置时,它被自动注册,当使用XML配置时,则通过context:annotation-config/或context:component-scan/元素手动注册。

返回值

被标注的方法可以没有返回值,也可以有返回值。当有返回值是,其返回值会被当作为一个新的事件发送。如果返回类型是数组集合,那么数组或集合中的每个元素都作为一个新的单独事件被发送。

异常处理

同步监听器抛出的所有checked异常都会被封装成 UndeclaredThrowableException ,因为事件发布者只能处理运行时异常(unchecked异常)。

异步监听器

当需要异步处理监听器时,可以在监听器方法上再增加另外的一个spring注解 @Async,但需要注意以下限制:

  1. 监听器报错不会传递给事件发起者,因为双方已经不在同一个线程了。
  2. 异步监听器的非空返回值不会被当作新的事件发布。如果需要发布新事件,需要注入 ApplicationEventPublisher后手动发布。

监听器排序

如果同一个事件可能会被多个监听器监听处理,那么我们可以使用 @Order 注解对各个监听器进行排序。

源码

package org.springframework.context.event;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import java.util.function.Predicate;import org.springframework.context.ApplicationEvent;import org.springframework.core.annotation.AliasFor;@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface EventListener {@AliasFor("classes")Class<?>[] value() default {};@AliasFor("value")Class<?>[] classes() default {};String condition() default "";String id() default "";}

使用示例

单一事件监听器

发布事件

@Servicepublic class EventPublisher {    private ApplicationEventPublisher  eventPublisher;    @Autowired    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {        this.eventPublisher = eventPublisher;    }    public void publishPersonSaveEvent(){        PersonSaveEvent  saveEvent = new PersonSaveEvent();        saveEvent.setId(1);        saveEvent.setName("i余数");        saveEvent.setAge(18);        eventPublisher.publishEvent(saveEvent);    }}

监听事件

@Slf4j@Servicepublic class EventListenerService {    @EventListener    public void handleForPersonSaveEvent(PersonSaveEvent saveEvent){        log.info("saveEvent -> {}", saveEvent);    }}

结果验证

saveEvent -> PersonSaveEvent(id=1, name=i余数, age=18)

使用classes实现多事件监听器

发布事件
在上一个示例的基础上,再多加一个PersonUpdateEvent事件。

public void publishPersonUpdateEvent(){    PersonUpdateEvent  updateEvent = new PersonUpdateEvent();    updateEvent.setId(1);    updateEvent.setName("i余数");    updateEvent.setAge(19);    eventPublisher.publishEvent(updateEvent);}

监听事件

@EventListener(classes = {PersonSaveEvent.class, PersonUpdateEvent.class})public void handleForPersonSaveAndUpdateEvent(Object event){    log.info("multi handle event -> {}", event);}

验证结果
可以监听到多个事件

multi handle event -> PersonSaveEvent(id=1, name=i余数, age=18)multi handle event -> PersonUpdateEvent(id=1, name=i余数, age=19)

使用condition筛选监听的事件

发布事件

public void publishPersonSaveEvent(){    PersonSaveEvent  saveEvent = new PersonSaveEvent();    saveEvent.setId(1);    saveEvent.setName("i余数");    saveEvent.setAge(18);    eventPublisher.publishEvent(saveEvent);    PersonSaveEvent  saveEvent2 = new PersonSaveEvent();    saveEvent2.setId(2);    saveEvent2.setName("i余数");    saveEvent2.setAge(18);    eventPublisher.publishEvent(saveEvent2);}

监听事件

@EventListener(condition = "#root.event.getPayload().getId() == 1")public void handleByCondition(PersonSaveEvent saveEvent){    log.info("只处理id等于1的 -> {}", saveEvent);}

结果验证
id为2的事件不满足条件,所以不会执行。

只处理id等于1的 -> PersonSaveEvent(id=1, name=i余数, age=18)

有返回值的监听器

返回一个单一对象

发布事件

public void publishPersonSaveEvent(){    PersonSaveEvent  saveEvent = new PersonSaveEvent();    saveEvent.setId(1);    saveEvent.setName("i余数");    saveEvent.setAge(18);    eventPublisher.publishEvent(saveEvent);}

监听事件

@EventListenerpublic void handleForPersonUpdateEvent(PersonUpdateEvent updateEvent){    log.info("handle update event -> {}", updateEvent);}@EventListenerpublic PersonUpdateEvent handleHaveReturn(PersonSaveEvent saveEvent){    log.info("handle save event -> {}", saveEvent);    PersonUpdateEvent updateEvent = new PersonUpdateEvent();    updateEvent.setId(saveEvent.getId());    updateEvent.setName(saveEvent.getName());    updateEvent.setAge(saveEvent.getAge());    return updateEvent;}

验证结果
可以看到我们监听到了2个事件,PersonSaveEvent是我们主动发布的事件,PersonUpdateEventhandleHaveReturn 方法的返回值,会被 Spring 自动当作一个事件被发送。

handle save event -> PersonSaveEvent(id=1, name=i余数, age=18)handle update event -> PersonUpdateEvent(id=1, name=i余数, age=18)

返回一个集合

将监听器稍作修改,使其返回一个集合。

@EventListenerpublic List<PersonUpdateEvent> handleHaveReturn(PersonSaveEvent saveEvent){    log.info("handle save event -> {}", saveEvent);    List<PersonUpdateEvent> events = new ArrayList<>();    PersonUpdateEvent updateEvent = new PersonUpdateEvent();    updateEvent.setId(saveEvent.getId());    updateEvent.setName(saveEvent.getName());    updateEvent.setAge(saveEvent.getAge());    events.add(updateEvent);    PersonUpdateEvent updateEvent2 = new PersonUpdateEvent();    BeanUtils.copyProperties(updateEvent, updateEvent2);    events.add(updateEvent2);    return events;}

查看结果可以发现,集合中的每个对象都被当作一个单独的事件进行发送。

handle save event -> PersonSaveEvent(id=1, name=i余数, age=18)handle update event -> PersonUpdateEvent(id=1, name=i余数, age=18)handle update event -> PersonUpdateEvent(id=1, name=i余数, age=18)

返回一个数组

和返回值为集合一样,数组中的每个对象都被当作一个单独的事件进行发送。

异步监听器

创建两个监听器,一个同步一个异步,异步监听器就是在方法上加一个 @Async 标签即可(你可以指定线程池)。

@EventListenerpublic void handleForPersonSaveEvent(PersonSaveEvent saveEvent){    log.info("handle event -> {}", saveEvent);}@Async@EventListenerpublic void handleForPersonSaveEventAsync(PersonSaveEvent saveEvent){    log.info("async handle event -> {}", saveEvent);}

从执行结果可以看出,异步线程是 task-1,不是主线程 main,即异步是生效的。

INFO 3851 --- [           main] i.k.s.e.listener.EventListenerService    : handle event -> PersonSaveEvent(id=1, name=i余数, age=18)INFO 3851 --- [         task-1] i.k.s.e.listener.EventListenerService    : async handle event -> PersonSaveEvent(id=1, name=i余数, age=18)

监听器异常处理

同步异常处理

使用 SimpleApplicationEventMulticaster 处理同步监听器抛出异常。
先定义一个ErrorHandler:

@Slf4j@Componentpublic class MyErrorHandler implements ErrorHandler {    @Override    public void handleError(Throwable t) {        log.info("handle error -> {}", t.getClass());    }}

将自定义ErrorHandler绑定到 SimpleApplicationEventMulticaster

@Slf4j@Servicepublic class EventListenerService {    @Autowired    private SimpleApplicationEventMulticaster simpleApplicationEventMulticaster;    @Autowired    private MyErrorHandler errorHandler;    @PostConstruct    public void init(){        simpleApplicationEventMulticaster.setErrorHandler(errorHandler);    }    @Order(1)    @EventListener    public void handleForPersonSaveEvent(PersonSaveEvent saveEvent) throws AuthException {        log.info("handle event -> {}", saveEvent);        throw new AuthException("test exception");    }}   

结果:可以看到捕获的异常是 UndeclaredThrowableException

handle event -> PersonSaveEvent(id=1, name=i余数, age=18)handle error -> class java.lang.reflect.UndeclaredThrowableException

异步异常处理

使用 SimpleAsyncUncaughtExceptionHandler 来处理 @Async 抛出的异常。

@Configurationpublic class AsyncConfig implements AsyncConfigurer {    @Override    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {        return new SimpleAsyncUncaughtExceptionHandler();    }}

监听器代码:人为的抛出一个异常。

@Async@EventListenerpublic void handleForPersonSaveEvent(PersonSaveEvent saveEvent) throws AuthException {    log.info("handle event -> {}", saveEvent);    throw new AuthException("test exception");}

结果: SimpleAsyncUncaughtExceptionHandler捕获到了 @Async 方法抛出的异常

 INFO 4416 --- [         task-1] i.k.s.e.listener.EventListenerService    : handle event -> PersonSaveEvent(id=1, name=i余数, age=18)ERROR 4416 --- [         task-1] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected exception occurred invoking async method: public void xxxx.handleForPersonSaveEvent(xxxx.PersonSaveEvent) throws javax.security.auth.message.AuthException

监听器排序

如果同时有多个监听器监听同一个事件,默认情况下监听器的执行顺序是随机的,如果想要他们按照某种顺序执行,可以借助Spring的另外一个注解 @Order 实现。

创建三个监听器,并使用@Order 排好序。

@Order(1)@EventListenerpublic void handleForPersonSaveEvent(PersonSaveEvent saveEvent){    log.info("handle event1 -> {}", saveEvent);}@Order(2)@EventListenerpublic void handleForPersonSaveEvent2(PersonSaveEvent saveEvent){    log.info("handle event2 -> {}", saveEvent);}@Order(3)@EventListenerpublic void handleForPersonSaveEvent3(PersonSaveEvent saveEvent){    log.info("handle event3 -> {}", saveEvent);}

从执行结果可以看到,确实是按照@Order中指定的顺序执行的。

handle event1 -> PersonSaveEvent(id=1, name=i余数, age=18)handle event2 -> PersonSaveEvent(id=1, name=i余数, age=18)handle event3 -> PersonSaveEvent(id=1, name=i余数, age=18)

来源地址:https://blog.csdn.net/u012359704/article/details/128569661

--结束END--

本文标题: Spring注解 @EventListener 的介绍与使用示例以及异常处理

本文链接: https://www.lsjlt.com/news/393126.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作