文章目录 基本概念用途事件处理条件处理器返回值异常处理异步监听器监听器排序源码 使用示例单一事件监听器使用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
,但需要注意以下限制:
如果同一个事件可能会被多个监听器监听处理,那么我们可以使用 @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)
发布事件
在上一个示例的基础上,再多加一个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)
发布事件
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
是我们主动发布的事件,PersonUpdateEvent
是 handleHaveReturn
方法的返回值,会被 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文档到电脑,方便收藏和打印~
2024-04-03
2024-04-03
2024-04-01
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0