深入理解Spring事件监听机制
深入理解Spring事件监听机制
在之前的文章中,我分享过一篇关于观察者模式的文章,如有有兴趣的可以去看一下,对理解Spring事件监听机制有些帮助。
言归正传,下面我们进行Spring事件监听机制的分享
1: Spring事件监听
Spring 的事件监听机制是在 JDK 事件监听的基础上进行的扩展,也是在典型观察者模式上的进一步抽象和改进。Spring 中提供了一套默认的事件监听机制,在容器初始化时便使用了这套机制。并且 Spring 也提供了事件监听机制的接口扩展能力,开发者基于此可快速实现自定义的事件监听功能
2: Spring事件监听的核心原理
Spring 的监听机制是非常优秀的思想,它能够很好地实现代码解耦,将业务逻辑与非业务逻辑分离,让程序变得更加灵活和可维护。
首先我们要理解几个概念
事件源: 事件的触发者,也即是事件发布者
事件: 描述发生了什么事情的对象,也就是我们事件的信息
事件监听器: 监听到事件的发生的时候,做一些处理
实现Spring事件机制主要有4个接口:
1: 事件源 ApplicationEventPublisher
作用: 用于发布事件
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
//发布事件
void publishEvent(Object event);
}
2: 事件监听器 ApplicationListener
作用: 监听对应的事件,做逻辑处理
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
//监听事件
void onApplicationEvent(E event);
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return event -> consumer.accept(event.getPayload());
}
}
3: 事件 ApplicationEvent
作用: 构造事件模型
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
//构造事件对象,带时间
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
//构造事件对象,定制时间区
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
//当前事件的时间
public final long getTimestamp() {
return this.timestamp;
}
}
4: 事件监听器管理 ApplicationEventMulticaster
作用: 负责事件的发布和监听
public interface ApplicationEventMulticaster {
//用于向事件广播器注册一个监听器。在广播器发送事件时,这个监听器将会接收到事件。
void addApplicationListener(ApplicationListener<?> listener);
//用于将一个已经注册的监听器从事件广播器中移除。
void removeApplicationListener(ApplicationListener<?> listener);
//用于移除所有注册的监听器。
void removeAllListeners();
//用于向所有已注册的监听器广播一个事件。
void multicastEvent(ApplicationEvent event);
}
3: 代码示例
了解上面的原理,我们看下在应用中是怎么使用的。
1: 创建监听类,将类注入到Spring容器中
@Component
public class OrderEventListener {
//这里使用了@Async注解异步线程执行,起作用的前提是开启@EnableAsync注解
@EventListener
@Async
public void handleOrderEvent(OrderEvent event) {
Order order = event.getOrder();
String message = "您有一个新的订单,订单号为:" + order.getOrderId();
System.out.println("message = " + message);
}
}
2: 创建事件模型,用于事件信息传输
public class OrderEvent extends ApplicationEvent {
public OrderEvent(Object source) {
super(source);
}
private Order order;
public OrderEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
3: 事件发布,将构造的事件发布出去
@RestController
@RequestMapping("/spring/event")
public class MyController {
@Autowired
private ApplicationEventPublisher eventPublisher;
@GetMapping("/order")
public void pushEvent() {
Order order = new Order();
order.setOrderId("1111111111111111111");
eventPublisher.publishEvent(new OrderEvent(this,order));
}
}
以上就是Spring 事件监听的简单使用,是不是非常的简单方便。
4: 源码理解
在看源码之前,我们先思考几个问题
- 监听器是如何注入的?
- Spring是怎么管理监听器和事件的对应的?
OK,那我们带着问题进入Spring源码中解读
1: EventListenerMethodProcessor
通过@EventListener注解的搜索发现,该类中的processBean()方法对该注解进行处理
private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
//将对应的监听器放入到Spring的applicationEventMulticaster中
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
2: ApplicationEventPublisher.publishEvent()
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//拿到所有的监听器,对事件进行处理
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
// 所有的监听器循环,匹配对应的事件
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//调用事件监听器
invokeListener(listener, event);
}
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//监听器分发事件, onApplicationEvent 就是我们自定义监听器的消费事件的地方
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
以上就是Spring事件监听的源码。
5: 优缺点
-
松耦合:通过事件监听机制,组件之间的耦合度得到了很大的降低,每个组件只需要监听自己感兴趣的事件,不需要关心其他组件的实现细节。
-
可扩展性:通过定义新的事件类型和监听器,应用程序的功能可以随着需求的变化而不断扩展。
-
高可靠性:事件监听机制可以保证应用程序的可靠性,即使某个组件出现异常或错误,其他组件仍然可以正常运行。
6: 总结
对于开发者来说,使用 Spring 监听机制非常简单。只需要实现事件和监听器接口,并在代码中注册监听器即可。Spring 会自动管理事件和监听器的生命周期,确保它们的正确运行。同时,由于 Spring 监听器使用了异步执行机制,因此不会影响主线程的运行效率,保证了应用程序的高并发和高效性。
本文来自博客园,作者:笨笨的二黄子,转载请注明原文链接:https://www.cnblogs.com/zwhdd/articles/17886985.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix