Spring事件机制@EventListener
@EventListener 看起来只是加了一个注解,Spring 到底在哪一刻、用什么机制把「事件」准确送到「标了注解的方法」里?
下面按 启动期 → 运行期 两条时间线,用“源码级”但“人话版”方式把完整链路拆开。
(代码行号基于 Spring Framework 6.1.x,5.x 原理完全一致)
一、启动期:把带 @EventListener 的方法“扫描”成“监听器”
-
入口:
EventListenerMethodProcessor- 实现了
SmartInitializingSingleton,容器在最后阶段会回调它的afterSingletonsInstantiated()。 - 核心逻辑:
遍历所有已创建的单例 Bean → 反射拿到所有方法 → 标了 @EventListener 的方法包装成 ApplicationListener 对象 → 注册到 ApplicationContext 的监听器列表里
- 实现了
-
关键源码片段(缩略)
public void afterSingletonsInstantiated() {
for (String beanName : beanFactory.getSingletonNames()) {
Class<?> targetType = beanFactory.getType(beanName);
// 1. 找到所有 @EventListener 方法
Map<Method, EventListener> annotatedMethods =
MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
// 2. 每个方法包一层 ApplicationListener
annotatedMethods.forEach((method, ann) -> {
ApplicationListener<?> listener =
createApplicationListener(beanName, targetType, method, ann);
// 3. 扔进容器
applicationContext.addApplicationListener(listener);
});
}
}
- 包装细节:
ApplicationListenerMethodAdapter- 把「方法对象 + 所在 Bean + 解析出来的条件表达式」缓存起来,等待运行期调用。
- 如果方法上还有
@Async,会再包一层AsyncExecutionInterceptor;
如果有@TransactionalEventListener,会换成TransactionalApplicationListener实现类。
二、运行期:一次 publishEvent() 的完整旅程
- 发布入口
ApplicationEventPublisher.publishEvent(Object event)
└→ AbstractApplicationContext.multicastEvent(ApplicationEvent, ResolvableType)
- 获取线程池(默认同步)
Executor executor = getTaskExecutor(); // 默认 null
如果配置了 SimpleApplicationEventMulticaster#setTaskExecutor(executor),就会异步。
-
遍历所有已注册的
ApplicationListener- 对于
@EventListener方式,此时拿到的就是前面扫描生成的ApplicationListenerMethodAdapter。 - 先执行
supportsEventType()做快速类型过滤:
把方法形参类型与事件实际类型做一次ResolvableType.isAssignableFrom()判断,不匹配直接跳过。 - 再执行
supportsSource()做源对象过滤(极少用)。 - 如果注解里写了
condition = "#event.xxx",则走 SpEL 计算:EvaluationContext context = new MethodBasedEvaluationContext(...); boolean pass = expressionParser.parseExpression(condition).getValue(context, Boolean.class);
- 对于
-
真正调用
- 反射
method.invoke(bean, event)。 - 如果方法返回类型不是
void,Spring 会把返回值再当成一个新事件做一次publishEvent()(事件链)。 - 如果方法标了
@Async,此时会触发AsyncExecutionInterceptor,把调用封装成Runnable丢进线程池。
- 反射
-
异常处理
- 同步调用:异常直接抛给发布者。
- 异步调用:异常被线程池捕获,通过
AsyncUncaughtExceptionHandler处理,默认只打日志。
三、事务监听 @TransactionalEventListener 的特殊分支
- 启动期同样由
EventListenerMethodProcessor识别,但会创建TransactionalApplicationListener实例。 - 运行期并不立即执行,而是:
publishEvent() 只是把监听器挂到 TransactionSynchronizationManager 当前事务提交/回滚后,TransactionSynchronization 回调触发真正的 invoke() - 阶段可选:
AFTER_COMMIT(默认)AFTER_ROLLBACKAFTER_COMPLETION(无论提交或回滚)BEFORE_COMMIT
四、一张图总结(文字版)
启动期
EventListenerMethodProcessor
扫描所有 Bean → @EventListener 方法
包装成 ApplicationListenerMethodAdapter
注册到 AbstractApplicationContext.listeners 列表
运行期
publishEvent()
multicastEvent()
for each ApplicationListener
类型过滤 → 条件过滤 → 反射 invoke()
├─ 同步:直接调用
├─ 异步:线程池
└─ 事务:挂到 TransactionSynchronization
五、回答最原始的问题
“实现原理”可以浓缩成两句话:
- 启动期 Spring 通过
EventListenerMethodProcessor把所有@EventListener方法反射包装成ApplicationListener对象,注册到容器。 - 运行期 每次
publishEvent()时,Spring 遍历这些监听器,先按事件类型 + SpEL 条件快速过滤,再反射(或线程池、或事务同步)调用目标方法,完成“发布-订阅”。
这就是 @EventListener 零接口、零配置却能生效的全部底层秘密。
原文地址:https://blog.csdn.net/zzz19920821/article/details/155743062
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!
