Spring Framework 7.0.9 深度剖析-14-事件机制
14.1 本章导读
Spring 应用里有一种最常见也最容易被"误用而不自知"的能力:事件(Application Event)。一个领域对象在服务方法末尾写下 publisher.publishEvent(new OrderCreatedEvent(order)),另一个角落的 @EventListener 方法就被触发——看起来像是一行代码的事,但它背后串起了 Observer 模式抽象、ResolvableType 泛型解析、监听器注册表与缓存、BeanPostProcessor 装配、SpEL 条件求值、事务同步注册等一整条链。这条链上的每一个"默认行为",几乎都对应着一个线上事故的高发点:
- 同步发布事件时,监听器里抛异常,发布者的事务直接回滚——这是默认行为,不是 bug;
- 给多播器配上
taskExecutor之后,监听器里读不到ThreadLocal里的事务上下文——因为异步分支切换了线程; @EventListener方法有返回值时,这个返回值会被当成新事件再次发布,新手往往浑然不觉;@TransactionalEventListener默认fallbackExecution = false,在没有事务的方法里调用publishEvent,监听器静默不执行;- 容器
refresh()完成之前发布的事件不会丢,而是先被收进earlyApplicationEvents,等监听器注册完毕后重放。
本章要做的,就是把这条链从入口到反射调用逐行拆开。涉及两个模块:事件核心在 spring-context(org.springframework.context 顶层包与 org.springframework.context.event 子包),事务事件在 spring-tx(org.springframework.transaction.event 包)。核心类包括 ApplicationEvent、ApplicationEventPublisher、ApplicationListener、ApplicationEventMulticaster 及其默认实现 SimpleApplicationEventMulticaster、注解驱动的 EventListenerMethodProcessor / ApplicationListenerMethodAdapter、以及事务侧的 TransactionalApplicationListenerMethodAdapter 与 TransactionalApplicationListenerSynchronization。
前置知识:读者应熟悉 Spring 容器 refresh() 的大致阶段(BeanFactory 准备、BeanFactoryPostProcessor、BeanPostProcessor、单例实例化)、ResolvableType 的基本用途(对泛型类型的封装)、以及 JDK 动态代理/CGLIB 代理的基本概念。读完本章,你应当能够:徒手画出一次 publishEvent 从方法调用到监听器反射执行的完整时序;说清 listenerCache(7.0.9 中是 retrieverCache)的 key/value 结构与失效时机;解释 @TransactionalEventListener(phase = AFTER_COMMIT) 在 7.0.9 中究竟挂在 TransactionSynchronization 的哪个回调上;并在排查"监听器为什么没执行/为什么抛异常炸了发布者"时,直接定位到本章对应的源码行。
版本事实先行:本章基于 spring-framework-7.0.9 仓库实际代码。与网上常见的 5.x/6.x 资料相比,7.0.9 有若干处值得注意的差异,后文会逐一点明,例如:ApplicationListenerDetector 在 7.0.9 中位于 spring-context 的 org.springframework.context.support 包而非 event 包;AFTER_COMMIT 阶段的实现挂在 afterCompletion(int status) 而非 afterCommit();ApplicationListener.supportsAsyncExecution()(6.1 引入)让"事务监听器必须跑在原线程"有了显式开关。
14.2 核心概念
14.2.1 ApplicationEvent:事件的抽象底座
所有 Spring 应用事件的父类是 ApplicationEvent,路径为 spring-context/src/main/java/org/springframework/context/ApplicationEvent.java。它本身极其克制——只比 JDK 的 java.util.EventObject 多了一个时间戳字段:
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;
}
}
逐点说明:
- 继承
EventObject。java.util.EventObject是 JDK 事件模型的根类,强制要求构造时传入source(事件源,即"在谁身上发生了这件事"),并提供getSource()。这就是 Observer 模式在 JDK 里的原生表达,Spring 没有另起炉灶。 timestamp是 final 不可变字段。构造即定格,getTimestamp()返回事件发生的毫秒时刻。第二个构造器接受Clock参数(5.3.8 引入),专门为测试场景注入可控时钟——这是框架级代码"可测试性优先"的典型手笔。- 类是
abstract的。注释写明:通用事件直接发布没有意义,必须由用户定义子类携带具体载荷。
由此引出一个重要结论:事件的类型信息完全靠 Java 类型系统承载。一个 OrderCreatedEvent 之所以被"监听订单事件的人"收到而不是被"监听发货事件的人"收到,靠的就是后续 ResolvableType 对这个类型做 isAssignableFrom 判定。这是整个事件匹配机制的根。
14.2.2 PayloadApplicationEvent:任意对象都能当事件
从 4.2 开始,Spring 允许你 publishEvent(Object)——传一个根本不继承 ApplicationEvent 的普通对象。容器会把它包进 PayloadApplicationEvent<T>(路径 spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java):
@SuppressWarnings("serial")
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
private final T payload;
private final ResolvableType payloadType;
public PayloadApplicationEvent(Object source, T payload) {
this(source, payload, null);
}
public PayloadApplicationEvent(Object source, T payload, @Nullable ResolvableType payloadType) {
super(source);
Assert.notNull(payload, "Payload must not be null");
this.payload = payload;
this.payloadType = (payloadType != null ? payloadType : ResolvableType.forInstance(payload));
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), this.payloadType);
}
public T getPayload() {
return this.payload;
}
}
两个关键点:
implements ResolvableTypeProvider。普通ApplicationEvent的泛型信息在运行时会被擦除,但PayloadApplicationEvent通过getResolvableType()主动回报"我持有的 payload 到底是什么ResolvableType"。这是后文"泛型事件匹配"能够成立的前提之一——payload 类型由ResolvableType.forInstance(payload)在包装时当场推断并固化下来,不依赖监听器一侧的泛型签名。- **三参构造器(6.0 引入)**允许发布方显式给出 payload 类型 hint。为什么需要?因为
payload.getClass()拿到的是运行时实际类型,而泛型声明(比如监听器要听List<OrderEvent>)匹配的是静态类型;当发布方手里的静态类型是List<OrderEvent>而运行时对象是ArrayList时,显式传入ResolvableType.forClassWithGenerics(List.class, OrderEvent.class)才能让匹配器拿到正确的泛型签名。
14.2.3 ApplicationEventPublisher:发布者的门面接口
发布侧的抽象只有一个接口 ApplicationEventPublisher(同包,ApplicationEventPublisher.java),它是 ApplicationContext 的父接口之一:
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
注意这里的三态在接口层就已经显形了:
publishEvent(ApplicationEvent event):default 方法,直接转型成Object转调——也就是说在接口层,"事件对象"和"任意载荷"已经被统一收敛到同一个出口;publishEvent(Object event):唯一的抽象方法,接受任意对象;- 真正带
ResolvableType的三参变体(publishEvent(Object event, ResolvableType typeHint))不在这个公共接口上,而是在AbstractApplicationContext中以protected形式提供(见 14.3 节源码),供容器层次结构之间传播时复用已解析的类型。
@FunctionalInterface 注解说明:你可以把它当成 lambda 类型来用(例如测试里 ApplicationEventPublisher publisher = e -> ...),但真实环境里它的实现永远是那个"什么都知道"的 ApplicationContext。
14.2.4 ApplicationListener 与它的三级扩展
监听侧的根接口是 ApplicationListener<E extends ApplicationEvent>(spring-context/src/main/java/org/springframework/context/ApplicationListener.java):
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
default boolean supportsAsyncExecution() {
return true;
}
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return event -> consumer.accept(event.getPayload());
}
}
逐点解读:
- 泛型参数
E就是监听器关心的事件类型。onApplicationEvent(E event)里的E在编译期决定了这个监听器对什么事件感兴趣——但这只是给人看的签名,运行时靠的是框架对这个泛型声明做反射解析(GenericApplicationListenerAdapter.resolveDeclaredEventType)。 - **
supportsAsyncExecution()(6.1 引入,默认true)**是一个容易被忽略的开关:即使给多播器配了异步taskExecutor,返回false的监听器也会被强制留在发布线程同步执行。事务事件监听器就靠这个方法硬编码返回false(见 14.6 节),保证事务同步回调必须运行在事务线程里。 - **静态工厂
forPayload(5.3 引入)**把"只关心 payload 的 Consumer"包成一个监听PayloadApplicationEvent<T>的 lambda 监听器——配合publishEvent(Object),用户连ApplicationEvent子类都不用写。
直接实现裸 ApplicationListener 时,事件类型过滤是"哑"的:你拿到所有事件,自己在方法里 instanceof 判断。为此 Spring 提供了两层更聪明的扩展接口:
第一层:SmartApplicationListener(spring-context/.../event/SmartApplicationListener.java,3.0 引入),它在裸接口之上暴露了两个元数据方法,让多播器不必把事件丢给监听器再由它自己忽略:
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
default String getListenerId() {
return "";
}
}
它的泛型参数被钉死成 ApplicationEvent(因为类型判断改由 supportsEventType 以 Class 形式回答),同时继承 Ordered——getOrder() 默认 LOWEST_PRECEDENCE(Integer.MAX_VALUE,即排在最后)。getListenerId()(5.3.5 引入)给监听器一个字符串身份,配合 removeApplicationListeners(Predicate) 做按条件摘除。
第二层:GenericApplicationListener(同包,4.2 引入,5.3.5 起正式继承 SmartApplicationListener)把类型判定从 Class 升级为 ResolvableType,从而能区分 OrderCreatedEvent 与 OrderCreatedEvent<List<Item>> 这种泛型差异:
public interface GenericApplicationListener extends SmartApplicationListener {
@Override
default boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return supportsEventType(ResolvableType.forClass(eventType));
}
boolean supportsEventType(ResolvableType eventType);
static <E extends ApplicationEvent> GenericApplicationListener forEventType(Class<E> eventType, Consumer<E> consumer) {
return new GenericApplicationListenerDelegate<>(eventType, consumer);
}
}
注意 default 方法把旧的 supportsEventType(Class) 桥接到新的 supportsEventType(ResolvableType)——这是接口演进的标准手法:老实现类不需要改代码,自动获得基于 ResolvableType 的入口。
对于那些只写了裸 ApplicationListener<SomeEvent> 的用户类,框架用 GenericApplicationListenerAdapter(同包)把它"包"成 GenericApplicationListener:在构造时反射解析出 declaredEventType(监听器泛型声明里的那个 E),之后 supportsEventType 就变成一句 this.declaredEventType.isAssignableFrom(eventType)。这个适配器是后文监听器筛选缓存的核心构件,14.5 节会完整剖析。
14.2.5 ApplicationEventMulticaster:事件的分发器
发布者(ApplicationContext)自己不直接决定"叫谁",它把这件事委托给 ApplicationEventMulticaster(spring-context/.../event/ApplicationEventMulticaster.java)。接口方法分两组:
- 注册/摘除组:
addApplicationListener(ApplicationListener)、addApplicationListenerBean(String)、removeApplicationListener(ApplicationListener)、removeApplicationListenerBean(String)、removeApplicationListeners(Predicate)、removeApplicationListenerBeans(Predicate)、removeAllListeners()。注意"实例"与"bean 名"是两套平行的注册通道——前者直接持有监听器对象,后者只记一个 bean 名字、等到真正分发时才向 BeanFactory 要实例(这是为了在容器还没实例化所有单例时就能登记监听器)。 - 分发组:
multicastEvent(ApplicationEvent)与multicastEvent(ApplicationEvent, ResolvableType)(4.2 引入,推荐使用,因为它带泛型类型信息)。
接口注释里有一句关键设计原则:"If a matching ApplicationListener does not support asynchronous execution, it must be run within the calling thread of this multicast call."——是否异步不是发布者决定的,而是监听器自己通过 supportsAsyncExecution() 表态、多播器执行时强制执行的。
至此,事件机制的五个核心抽象全部就位:ApplicationEvent(事件)、ApplicationEventPublisher(发布门面)、ApplicationListener 及其扩展(监听方)、ApplicationEventMulticaster(分发器)、以及把后两者粘进容器的 AbstractApplicationContext。下面进入源码剖析。
14.3 源码剖析(一):发布入口 publishEvent 的全流程
14.3.1 两个公开重载与一个 protected 三参方法
一切从 AbstractApplicationContext.publishEvent 开始(路径 spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java)。公开层只有两个薄封装:
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
@Override
public void publishEvent(Object event) {
publishEvent(event, null);
}
真正干活的是 protected void publishEvent(Object event, @Nullable ResolvableType typeHint)(第 414-460 行,7.0.9 实际源码)。这是本章最重要的一段方法,逐段贴出并解读:
protected void publishEvent(Object event, @Nullable ResolvableType typeHint) {
Assert.notNull(event, "Event must not be null");
ResolvableType eventType = null;
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent applEvent) {
applicationEvent = applEvent;
eventType = typeHint;
}
else {
ResolvableType payloadType = null;
if (typeHint != null && ApplicationEvent.class.isAssignableFrom(typeHint.toClass())) {
eventType = typeHint;
}
else {
payloadType = typeHint;
}
applicationEvent = new PayloadApplicationEvent<>(this, event, payloadType);
}
// Determine event type only once (for multicast and parent publish)
if (eventType == null) {
eventType = ResolvableType.forInstance(applicationEvent);
if (typeHint == null) {
typeHint = eventType;
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {
abstractApplicationContext.publishEvent(event, typeHint);
}
else {
this.parent.publishEvent(event);
}
}
}
第一段:事件包装(Decorate)。传入的 event 有三种身份:
- 它本身就是
ApplicationEvent——直接使用,eventType直接采用调用方给的typeHint(通常是 null); - 它是个普通对象,且调用方给的
typeHint恰好指向一个ApplicationEvent子类(这种情况来自父子容器传播路径,下面会讲)——直接把 hint 当事件类型; - 最常见的情况:普通对象、无 hint——包装成
new PayloadApplicationEvent<>(this, event, payloadType)。注意构造的第一个参数this是事件源(source),即"事件由当前容器发出";payloadType 在无 hint 时为 null,由PayloadApplicationEvent内部ResolvableType.forInstance(payload)自行推断。
第二段:类型只解析一次。eventType 为 null 时,用 ResolvableType.forInstance(applicationEvent) 现场解析。这里把"为多播解析类型"和"为父容器传播解析类型"合并成一次计算——注释明说 "Determine event type only once (for multicast and parent publish)"。对 PayloadApplicationEvent 来说,forInstance 触发的正是 ResolvableTypeProvider.getResolvableType(),于是泛型 payload 的类型被完整保留(14.2.2 节埋的伏笔在此回收)。
第三段:立刻多播 or 进早期队列。这是整个容器事件生命周期最精妙的分支:
this.earlyApplicationEvents != null:说明容器还处在prepareRefresh()之后、registerListeners()之前的窗口里——多播器刚建好但监听器一个都没注册,此时多播等于白发,于是把事件追加进earlyApplicationEvents这个LinkedHashSet,留待监听器注册完后重放;- 否则,只要多播器存在,就立刻
multicastEvent(applicationEvent, eventType)。
earlyApplicationEvents 字段在 7.0.9 中的声明(第 243-244 行):
/** ApplicationEvents published before the multicaster setup. */
private @Nullable Set<ApplicationEvent> earlyApplicationEvents;
它是 @Nullable——这个 null 本身就是状态机开关:refresh 过程中被置为一个空 LinkedHashSet(非 null = "正在收集早期事件"),registerListeners() 里重放完毕后被显式置回 null("窗口关闭,此后发布立即多播")。用 null/非 null 兼任布尔标志位,是 Spring 容器的经典手法。
第四段:向父容器冒泡。this.parent != null 时,同一个事件会沿容器层次向上传播。注意细节:
- 传下去的是**原始
event(不是包装后的 applicationEvent)**和已经解析好的typeHint; - 若父容器也是
AbstractApplicationContext,递归调用publishEvent(event, typeHint),复用刚才算好的类型,避免父容器重复解析; - 若父容器不是
AbstractApplicationContext(比如自定义实现),退化为parent.publishEvent(event),不带类型提示。
这条冒泡链意味着:子容器发布的事件,父容器里的监听器也能收到,反之则不然(父容器不会反向通知子容器)。这在 Spring Boot 的"父子上下文 + 监听器"排障中是高频考点(见 14.9 节)。
14.3.2 与多播器之间的两个胶水方法
容器持有多播器的字段与访问方法(第 231-232 行、467-473 行):
/** Helper class used in event publishing. */
private @Nullable ApplicationEventMulticaster applicationEventMulticaster;
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
包级可见(package-private),供容器内部(如 registerListeners、ApplicationListenerDetector)调用;外部用户拿不到它。注意它在多播器为 null 时直接抛 IllegalStateException——这就是"refresh 之前调 publishEvent 却又没走早期事件队列"时的兜底异常。
对外的监听器登记入口 addApplicationListener(第 556-562 行)同样是双写:
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationEvent must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
逐行解读:
- 多播器已存在 → 立刻登记进去(运行期动态加监听器生效);
- 无论多播器是否存在,都往
applicationListeners这个LinkedHashSet里加一份。这个集合是容器级"静态监听器清单",在prepareRefresh()时会被快照到earlyApplicationListeners,并在registerListeners()时逐批同步给多播器。
为什么要双写?因为 addApplicationListener 可能在 refresh 的任何阶段被调用:
- 在
initApplicationEventMulticaster()之前调用:多播器还是 null,只能先记在容器自己的applicationListeners里,等registerListeners()统一入账; - 在 refresh 完成之后调用:多播器已就绪,两边同时更新,保持"容器清单 = 多播器清单"的一致性,这样下一次
refresh()(ConfigurableApplicationContext 允许重启)还能凭earlyApplicationListeners重建现场。
applicationListeners 字段声明(第 237-238 行):
/** Statically specified listeners. */
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
LinkedHashSet 保证登记顺序可预测——这与后文 AnnotationAwareOrderComparator.sort 的排序行为配合时,"order 相同的监听器保持登记顺序"就是确定的。
14.3.3 框架自身发布的事件
容器自己在生命周期节点上发布 7 种标准事件,全部位于 spring-context/.../event/ 包,都继承自 ApplicationContextEvent(携带容器引用作为 source):
| 事件类 | 发布时机(AbstractApplicationContext 中的调用点) |
|---|---|
ContextRefreshedEvent | finishRefresh() 末尾,容器就绪(第 1016 行) |
ContextClosedEvent | doClose() 中,关闭流程开始(第 1190 行附近) |
ContextStartedEvent | start()(第 1585 行) |
ContextStoppedEvent | stop()(第 1591 行) |
ContextRestartedEvent | restart()(第 1597 行) |
ContextPausedEvent | pause()(第 1603 行) |
MethodFailureEvent | ApplicationListenerMethodAdapter 反射调用失败时(14.7 节会提及其用途) |
ContextRefreshedEvent 的发布点值得单独看一眼(第 1002-1017 行):
protected void finishRefresh() {
// Reset common introspection caches in Spring's core infrastructure.
resetCommonCaches();
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
}
注意它在 onRefresh()(LifecycleProcessor 的启动回调)之后才发布——所以监听 ContextRefreshedEvent 的 bean 一定能看到所有 SmartLifecycle bean 已经启动完毕。这种"生命周期动作与事件发布的先后次序"是容器级设计的细节,用户代码里常常依赖它。
14.4 源码剖析(二):容器如何装配出事件系统
14.4.1 refresh() 中的三个事件相关节点
AbstractApplicationContext.refresh() 的主流程(第 597-625 行节选)里,与事件直接相关的有三步,顺序钉死:
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
为什么多播器必须在 initMessageSource() 之后、registerListeners() 之前初始化?看 initMessageSource() 的注释就明白:监听器实现里可能想注入 MessageSource 做国际化日志,所以监听器必须排在 MessageSource 之后;而多播器是监听器的"收件箱",必须排在监听器之前。这是一条严格的拓扑序。
而整个"早期事件窗口"的打开,发生在更早的 prepareRefresh()(第 689-701 行):
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
逐行解读:
earlyApplicationListeners快照。第一次 refresh 时把当前applicationListeners(用户在 context.refresh() 之前手动addApplicationListener进去的那些)快照保存;如果是第二次 refresh(容器重启),则把applicationListeners重置为快照内容——清除上一轮 refresh 期间动态加的监听器,回到"refresh 前的静态状态"。- 打开早期事件收集窗口:
earlyApplicationEvents = new LinkedHashSet<>()。从这一刻起,任何对publishEvent的调用(包括BeanFactoryPostProcessor、BeanPostProcessor注册过程中触发的事件)都会被收进这个集合而不是立刻多播——因为此刻多播器还没建、监听器也没注册,播了也没人听。
这个窗口一直开到 registerListeners() 末尾才关闭(下一节看到)。也就是说:从 prepareRefresh() 到 registerListeners() 之间发生的所有事件,都会被缓存并重放。
14.4.2 initApplicationEventMulticaster:默认多播器从哪来
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
APPLICATION_EVENT_MULTICASTER_BEAN_NAME 的值(第 165 行)就是字符串 "applicationEventMulticaster"。逻辑分两路:
- 容器里有名为
applicationEventMulticaster的 bean:getBean拿到它作为多播器。这是用户替换默认实现的扩展点——你可以自己定义一个ApplicationEventMulticaster实现并注册成这个名字(注意必须是 local bean,containsLocalBean不查父容器,所以父容器配的多播器不会被子容器继承)。 - 没有:
new SimpleApplicationEventMulticaster(beanFactory)现场构造一个,并通过beanFactory.registerSingleton(...)把它登记成单例——这样用户后续可以按名字getBean("applicationEventMulticaster")拿到它、给它setTaskExecutor。
注意默认构造走的是 SimpleApplicationEventMulticaster(BeanFactory) 构造器(见 14.5 节),它立刻把 BeanFactory 关联上——因为多播器在按 bean 名解析监听器时需要它。此时 taskExecutor 与 errorHandler 都是 null,即默认同步、默认异常直接抛。
14.4.3 registerListeners:三条注册通道 + 早期事件重放
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
逐段拆解:
第一条通道:静态监听器实例。getApplicationListeners() 返回容器级 applicationListeners 集合(用户在 refresh 前手动加的、或 AbstractApplicationContext 子类构造时加的),逐个 addApplicationListener 进多播器。
第二条通道:监听器 bean 的名字。getBeanNamesForType(ApplicationListener.class, true, false)——第二个参数 allowFactoryBeanInit = true,第三个 includeNonSingletons = false。关键注释写得很清楚:"Do not initialize FactoryBeans here: We need to leave all regular beans uninitialized to let post-processors apply to them!"——这里只拿 bean 名字,绝不提前实例化任何监听器 bean(否则它们会绕过 BeanPostProcessor,@Transactional、@Async 这些代理都不会生效)。拿到名字后只调 addApplicationListenerBean(listenerBeanName)——多播器把名字存进 applicationListenerBeans 集合,等到真正分发事件时才 beanFactory.getBean(name, ApplicationListener.class) 现取。
这就是"实例"与"bean 名"双通道的意义:addApplicationListener 持有的是对象本身(如 @EventListener 方法被 EventListenerMethodProcessor 包装成的 ApplicationListenerMethodAdapter,它根本不是容器里的 bean);addApplicationListenerBean 持有的是名字(如用户写的 MyListener implements ApplicationListener 组件,它需要在分发时才被容器正确实例化、织入代理)。
第三步:关闭窗口、重放早期事件。先把集合引用取出来存到局部变量 earlyEventsToProcess,再把字段置 null——这个顺序很关键:置 null 之后,重放过程中如果又有事件被发布(比如某个早期监听器在重放时发布了新事件),那些新事件会走 else if (multicaster != null) 分支立即多播,而不是被再次吞进集合造成无限缓冲。
随后按 LinkedHashSet 的插入顺序逐个 multicastEvent(earlyEvent)。注意:重放时用的是单参 multicastEvent(event),不带 ResolvableType——类型由方法内部 ResolvableType.forInstance(event) 现场推断,对普通 ApplicationEvent 没有问题;对 payload 事件,PayloadApplicationEvent.getResolvableType() 已在包装时固化,也能正确还原。
还有一个微妙点:重放发生在 finishBeanFactoryInitialization(实例化所有单例)之前。也就是说,早期事件重放时,绝大多数业务单例还没创建,按 bean 名注册的监听器此刻被 getBean 触发实例化——它们会走完整的 bean 创建流程(包括 BeanPostProcessor)。而 @EventListener 方法是在 afterSingletonsInstantiated() 阶段(所有单例就绪后)才被 EventListenerMethodProcessor 收集成监听器的——所以早期事件重放时,注解监听器大概率还没登记,收不到这些事件。这是早期事件生命周期里最重要的边界,14.8 节时序图会再次标注。
14.4.4 ApplicationListenerDetector:第三条通道的运行时兜底
还有一类监听器,getBeanNamesForType 在 registerListeners() 那个时点查不出来——比如内层 bean(inner bean)、或者类型在那时还不可知的监听器。框架用一个 BeanPostProcessor 在每个 bean 初始化完成的瞬间做兜底捕获。这个检测器在 7.0.9 中的位置是 spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java(注意:在 support 包,不在 event 包):
class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class);
private final transient AbstractApplicationContext applicationContext;
private final transient Map<String, Boolean> singletonNames = new ConcurrentHashMap<>(256);
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (ApplicationListener.class.isAssignableFrom(beanType)) {
this.singletonNames.put(beanName, beanDefinition.isSingleton());
}
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener<?> applicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
// singleton bean (top-level or inner): register on the fly
this.applicationContext.addApplicationListener(applicationListener);
}
else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
this.singletonNames.remove(beanName);
}
}
return bean;
}
逐段解读:
postProcessMergedBeanDefinition预埋标记。在 bean 定义合并完成后(早于实例化),凡是类型实现了ApplicationListener的 bean,记下它是不是 singleton。这张singletonNames表是后面判定的依据。postProcessAfterInitialization捕获。bean 走完初始化(含@PostConstruct)之后,如果它确实是个ApplicationListener:标记为 TRUE 的(单例)→ 立刻addApplicationListener登记进容器;标记为 FALSE 的(非单例,典型如 request/session 域的内层 bean)→ 只打一条警告"它无法可靠地参与事件多播",然后从表里摘除。- 为什么非单例监听器不能登记?因为多播器按 bean 名缓存监听器时,非单例 bean 每次
getBean都返回新实例——事件分发时取到的是一个临时实例,处理完就丢弃,状态毫无意义。框架的选择是:非单例监听器只允许顶层 bean 存在(那时getBeanNamesForType已经把它登记成 bean 名监听器,分发时每次现取新实例),内层的非单例监听器直接警告并放弃。 - 销毁时摘除(
postProcessBeforeDestruction):监听器 bean 销毁时从多播器移除实例和对应 bean 名,避免容器关闭阶段还往一个已销毁的 bean 上发事件。
至此,监听器的三条登记通道全部清晰:
通道一:context.addApplicationListener(实例) —— 静态/编程式,进 applicationListeners + 多播器
通道二:实现 ApplicationListener 的组件 bean —— registerListeners() 按名字登记,分发时 getBean
通道三:@EventListener 方法 —— EventListenerMethodProcessor 包装成适配器后走通道一
(兜底):ApplicationListenerDetector —— bean 初始化后捕获漏网的单例监听器
14.5 源码剖析(三):监听器的解析与缓存——AbstractApplicationEventMulticaster
多播的核心问题不是"调用监听器",而是"哪些监听器该被调用"。这个问题在 7.0.9 中由 AbstractApplicationEventMulticaster(路径 spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java)解决,它的 retrieverCache 就是任务描述中提到的 listenerCache。
14.5.1 两个 Retriever 与一张缓存表
先看字段区(第 70-76 行):
private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
private @Nullable ClassLoader beanClassLoader;
private @Nullable ConfigurableBeanFactory beanFactory;
defaultRetriever:权威注册表。内部两个LinkedHashSet——applicationListeners(监听器实例集合)与applicationListenerBeans(监听器 bean 名集合)。所有 add/remove 都在synchronized (this.defaultRetriever)保护下写这两个集合,并在变更后this.retrieverCache.clear()(缓存整体失效)。retrieverCache:结果缓存。key 是ListenerCacheKey(事件类型 + 事件源类型),value 是CachedListenerRetriever(已经过滤好的监听器子集)。用ConcurrentHashMap,说明读多写少、并发读取无锁。
注册方法的写法(第 105-116 行)很能说明问题:
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.defaultRetriever) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
注意 AOP 去重:如果传入的是个代理对象(比如监听器类被 @Transactional 织入了 CGLIB 代理),getSingletonTarget 剥出原始目标;若目标本身已在集合里,先移除目标——避免"代理 + 裸目标"两个实例同时登记导致同一个监听器逻辑被执行两遍。任何注册变更都清空整张 retrieverCache——因为缓存的是"某事件类型下该叫谁"的预过滤结果,注册集合一变,所有预过滤结果都作废。这是保守但正确的选择:监听器注册是低频操作,清空缓存的成本远低于维持缓存一致性的成本。
14.5.2 getApplicationListeners:缓存查找的三段式
分发时调用的入口是 getApplicationListeners(ApplicationEvent event, ResolvableType eventType)(第 188-223 行):
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Potential new retriever to populate
CachedListenerRetriever newRetriever = null;
// Quick check for existing entry on ConcurrentHashMap
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever == null) {
// Caching a new ListenerRetriever if possible
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
newRetriever = new CachedListenerRetriever();
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null; // no need to populate it in retrieveApplicationListeners
}
}
}
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
// If result is null, the existing retriever is not fully populated yet by another thread.
// Proceed like caching wasn't possible for this current local attempt.
}
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
逐段解读:
- 构造 cacheKey:key 由两部分组成——事件的
ResolvableType(含泛型)与事件源的Class(source.getClass(),注意是运行时 source 类型,不是 null)。ListenerCacheKey(第 405-448 行)实现了equals/hashCode:事件类型逐字段相等 + source 类型 nullSafe 相等。这意味着"同一事件类型、同一 source 类型"的多次发布命中同一条缓存。 - 快路径查缓存:
retrieverCache.get(cacheKey)。 - miss 时的并发保护:只有"类加载安全"(
ClassUtils.isCacheSafe——事件类与 source 类都由 bean ClassLoader 或其父加载,不会在热部署/多类加载器场景下造成类泄漏)时才建CachedListenerRetriever,用putIfAbsent占位。两个并发线程同时 miss 同一 key 时,只有一个线程的 retriever 会留下(existingRetriever != null时把newRetriever置 null,表示"我不需要填充它了")。 - 双检取值:拿到 existingRetriever 后调它的
getApplicationListeners()。这个方法返回 null 表示"另一个线程还没填充完"——此时当前线程不阻塞,而是退化为现场计算(把 newRetriever 当 null 传,结果不入缓存)。这是典型的"缓存未就绪时降级直算"策略,避免锁竞争。
14.5.3 retrieveApplicationListeners:真实的过滤与排序
未命中缓存时走私有方法 retrieveApplicationListeners(第 233-327 行),这是监听器筛选的完整逻辑:
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// Add programmatically registered listeners, including ones coming
// from ApplicationListenerDetector (singleton beans and inner beans).
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
filteredListeners.add(listener);
}
allListeners.add(listener);
}
}
// Add listeners by bean name, potentially overlapping with programmatically
// registered listeners above - but here potentially with additional metadata.
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
// ...代理去重逻辑...
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
filteredListeners.add(listener);
}
else {
filteredListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
else {
// Remove non-matching listeners that originally came from
// ApplicationListenerDetector, possibly ruled out by additional
// BeanDefinition metadata (for example, factory method generics) above.
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
filteredListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null) {
if (CollectionUtils.isEmpty(filteredListenerBeans)) {
retriever.applicationListeners = new LinkedHashSet<>(allListeners);
retriever.applicationListenerBeans = filteredListenerBeans;
}
else {
retriever.applicationListeners = filteredListeners;
retriever.applicationListenerBeans = filteredListenerBeans;
}
}
return allListeners;
}
逐段拆解:
- 快照注册表。在
synchronized (this.defaultRetriever)里把两个集合复制出来——锁只保护这一次复制,后面的过滤、getBean都在锁外做,避免"持有注册表锁去实例化 bean"造成死锁(实例化 bean 又可能反过来注册监听器)。 - 实例监听器的一轮过滤:逐个
supportsEvent(listener, eventType, sourceType)判断。命中就进allListeners;若本次要建缓存(retriever != null),同时进filteredListeners。 - bean 名监听器的两轮过滤:
- 第一轮是免实例化的预检
supportsEvent(beanFactory, listenerBeanName, eventType)(第 343-363 行):先beanFactory.getType(name)拿到监听器类;如果它是GenericApplicationListener/SmartApplicationListener或者类型拿不到,直接放行(留到实例化后细判);否则用监听器类的声明事件泛型与事件类型做一次isAssignableFrom,不匹配的直接跳过、连 bean 都不实例化。这是关键优化:按名字登记的监听器 bean 在大多数事件下连 getBean 都省了。 - 第二轮是实例化后细判:
beanFactory.getBean(name, ApplicationListener.class)拿到实例,再跑一遍supportsEvent(listener, eventType, sourceType)。 - 中间还夹着一段 AOP 去重:
AopProxyUtils.getSingletonTarget(listener)剥出代理背后的裸目标,如果裸目标已在allListeners里(它可能是被ApplicationListenerDetector捕获登记的),就用代理替换裸目标——防止同一逻辑执行两遍。 - 非单例监听器 bean 特殊处理:缓存时只记它的名字(
filteredListenerBeans)而不记实例——因为非单例每次都得现取新实例,缓存实例没有意义;这也是CachedListenerRetriever为什么同时持有applicationListeners与applicationListenerBeans两个字段的原因。
- 第一轮是免实例化的预检
- 排序:
AnnotationAwareOrderComparator.sort(allListeners)——把@Order/Ordered的语义落到分发顺序上(14.7 节详述)。 - 写回缓存:如果存在非单例 bean 监听器,缓存只保存实例子集(filteredListeners)+ bean 名子集;否则缓存保存完整结果。
CachedListenerRetriever的两个字段都是volatile——写一次、之后只读,volatile 保证其他线程立即可见。
14.5.4 supportsEvent 的三层判定
supportsEvent(listener, eventType, sourceType)(第 393-399 行)极简:
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener gal ? gal :
new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
任何监听器,只要它本身不是 GenericApplicationListener,就当场包一个 GenericApplicationListenerAdapter。适配器内部(GenericApplicationListenerAdapter.java 第 69-80 行)三选一:
@Override
public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof GenericApplicationListener gal) {
return gal.supportsEventType(eventType);
}
else if (this.delegate instanceof SmartApplicationListener sal) {
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
return (eventClass != null && sal.supportsEventType(eventClass));
}
else {
return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
}
}
- 目标是
GenericApplicationListener:直接用它自己的ResolvableType判定; - 目标是老式
SmartApplicationListener:把ResolvableType解析成Class喂给旧接口; - 目标是裸
ApplicationListener:用构造时反射解析好的declaredEventType(泛型E)做isAssignableFrom。
declaredEventType 的解析(第 109-116 行)用 ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric()——取"这个类实现 ApplicationListener 时声明的那个泛型参数",并用 eventTypeCache(ConcurrentReferenceHashMap,软引用值)按监听器类缓存解析结果。若解析出来还是 ApplicationEvent 本身(比如写了 ApplicationListener raw 裸类型),再通过 AopUtils.getTargetClass 剥一层代理重新解析。
这套机制解释了一个经典问题:为什么裸 ApplicationListener<OrderCreatedEvent> 能自动收到 OrderCreatedEvent——就是 declaredEventType.isAssignableFrom(eventType) 在起作用。
14.6 源码剖析(四):SimpleApplicationEventMulticaster 的分发动作
筛选出该叫谁之后,真正"挨个叫人"的是 SimpleApplicationEventMulticaster(路径 spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java)。它继承 AbstractApplicationEventMulticaster,只负责两件事:决定同步还是异步执行、决定异常如何处理。
14.6.1 两个可配字段:taskExecutor 与 errorHandler
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
private @Nullable Executor taskExecutor;
private @Nullable ErrorHandler errorHandler;
private volatile @Nullable Log lazyLogger;
taskExecutor:默认 null。javadoc 明确说默认等价于SyncTaskExecutor——在调用线程里同步跑完所有监听器。配上ThreadPoolTaskExecutor之类,监听器就进线程池跑。errorHandler:默认 null。javadoc 说得更直白:默认情况下"监听器异常会中断当前多播、并传播给事件发布者";配了 taskExecutor 之后,单个监听器的异常传播给执行器,但不必然中断其他监听器。lazyLogger:日志对象延迟初始化(volatile),避免构造期就绑定日志系统。
两个 setter(setTaskExecutor/setErrorHandler)就是全部扩展点——用户拿到容器里那个名为 applicationEventMulticaster 的 bean 后调它们即可:
applicationContext.getBean("applicationEventMulticaster", SimpleApplicationEventMulticaster.class)
.setTaskExecutor(myExecutor);
14.6.2 multicastEvent:同步/异步分支的完整判定
单参重载直接转双参(第 132-134 行):
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, null);
}
核心双参方法(第 137-154 行,7.0.9 实际源码):
@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null && listener.supportsAsyncExecution()) {
try {
executor.execute(() -> invokeListener(listener, event));
}
catch (RejectedExecutionException ex) {
// Probably on shutdown -> invoke listener locally instead
invokeListener(listener, event);
}
}
else {
invokeListener(listener, event);
}
}
}
逐段解读:
- 补全类型:
eventType为 null 时ResolvableType.forInstance(event)现场解析——与publishEvent里的逻辑呼应(走publishEvent主路径时类型早已算好传进来,这里只是兜底)。 - 取出筛选结果:
getApplicationListeners(event, type)——上一节详解的带缓存方法。 - 逐监听器分发,两道闸门决定同步还是异步:
- 闸门一
executor != null:多播器整体配了线程池; - 闸门二
listener.supportsAsyncExecution():这个监听器自己表态同意异步。 两个条件同时满足才进executor.execute(...);否则一律同步invokeListener(listener, event)。
- 闸门一
- 异步拒绝的兜底:线程池关闭中抛
RejectedExecutionException时,注释写得很直白——"Probably on shutdown",此时退化为本地同步执行该监听器,保证"容器关闭阶段发布的关闭事件,监听器至少不会丢"。 - 事务监听器为何强制同步:
TransactionalApplicationListener.supportsAsyncExecution()硬编码返回false(14.7 节会看到)——哪怕用户配了全局 taskExecutor,事务事件监听器的"注册事务同步"动作也必须发生在发布线程(否则TransactionSynchronizationManager这个 ThreadLocal 里根本没有当前事务)。
14.6.3 invokeListener:异常处理的两道分支
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
逻辑对称:配了 errorHandler——任何 Throwable 都交给它处置(常见实现 TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER 就是打日志后吞掉);没配——doInvokeListener 裸调,异常原样向外抛。注意无 errorHandler 时抛异常会怎样:它会直接逃出 multicastEvent 的 for 循环——排在后面的监听器不再执行,异常继续沿调用栈传回 publishEvent、传回发布者业务方法。这就是"同步监听器抛异常导致发布者中断"的源码出处。
14.6.4 doInvokeListener:ClassCastException 的智能容错
真正的反射/直接调用在 doInvokeListener(第 178-202 行):
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent payloadEvent &&
matchesClassCastMessage(msg, payloadEvent.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 = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
这段是整个事件机制里最容易被忽略、但最实用的防御设计:
- 为什么会有 ClassCastException? 考虑 lambda 监听器:
ApplicationListener<OrderCreatedEvent> l = e -> ...。lambda 在编译后没有"泛型父类"可反射——GenericApplicationListenerAdapter.resolveDeclaredEventType解析它的 class 时拿不到OrderCreatedEvent,只能放行所有事件(declaredEventType == null时 supportsEventType 返回 true)。于是一个本应只处理OrderCreatedEvent的 lambda,可能被丢进来一个UserDeletedEvent,方法体里强转时炸出ClassCastException。 - 容错策略:框架去匹配异常消息文本(
matchesClassCastMessage),判断这个 CCE 是不是"事件类型本身不匹配"引起的——是,则降级为 trace 日志吞掉(相当于这个事件"不该我听");不是(比如是监听器内部别的类型转换炸了),原样抛出。 - 消息匹配(
matchesClassCastMessage,第 204-220 行)要兼容三个 JDK 版本的异常文案格式:Java 8 的"java.lang.String cannot be cast..."、Java 11 的"class ..."、Java 9 带模块名的"java.base/java.lang.String cannot be cast..."——分别用 startsWith 类名、startsWith 类 toString、以及/分隔符后再比对类名来识别。
这个设计的实践含义很重要:你写的裸 lambda 监听器永远不会因为"收到了不该收的事件"而炸出可见异常——不匹配的事件会被 trace 级日志静默吞掉。但反过来说,如果你在监听器里看到业务逻辑压根没跑、日志里又只有一条 trace,别怀疑框架——先检查自己是不是 lambda 丢了泛型签名。
14.6.5 分发小结:一次 multicastEvent 的动作清单
把 14.5 与本节串起来,multicastEvent(event, type) 完成的动作是:
multicastEvent(event, type)
├─ ResolvableType type 补全(如缺失)
├─ getApplicationListeners(event, type)
│ ├─ ListenerCacheKey(eventType, sourceType) 查 retrieverCache
│ ├─ 命中 → CachedListenerRetriever.getApplicationListeners()(非单例 bean 现取 + 排序)
│ └─ 未命中 → 快照注册表 → supportsEvent 三层过滤 → AnnotationAwareOrderComparator.sort → 回填缓存
└─ for each listener:
├─ executor && listener.supportsAsyncExecution() → executor.execute(() -> invokeListener)
│ └─ RejectedExecutionException → 本地兜底 invokeListener
└─ 否则 → 同步 invokeListener
└─ errorHandler != null ? try/catch(Throwable).handleError : doInvokeListener
└─ listener.onApplicationEvent(event) + CCE 智能容错
这套设计的性能特征也清楚了:稳态下(无注册变更、事件类型重复出现),第二次发布同一类型事件的开销约等于一次 ConcurrentHashMap 查找 + 遍历已排好序的监听器列表——泛型匹配、排序全部被缓存,这就是 Spring 事件在高频发布场景下依然够用的原因。
14.7 源码剖析(五):@EventListener 注解监听器的装配链
14.7.1 注解本身:EventListener
@EventListener 的定义(spring-context/src/main/java/org/springframework/context/event/EventListener.java)很短,但每个属性都对应一条运行时行为:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective
public @interface EventListener {
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String condition() default "";
boolean defaultExecution() default true;
String id() default "";
}
classes/value:显式声明要监听的事件类型数组。指定后,方法参数必须是这些类的公共基类;不指定时,事件类型从方法唯一的参数签名推断。condition:SpEL 条件,求值为 true(或 "true"/"on"/"yes"/"1")才处理。可用变量见 javadoc:#root.event、#root.args、#a0/#p0、参数名。defaultExecution(6.2 引入):"没有事务等前置条件时是否默认执行"——这个属性是为@TransactionalEventListener的fallbackExecution做桥接而存在的。id:监听器身份,默认是全限定方法签名。- 元注解
@Reflective:AOT 处理用,提示这个注解需要反射保留。 - 最重要的一条设计决策写在类级 javadoc 里:"Annotated methods may have a non-void return type. When they do, the result of the method invocation is sent as a new event. If the return type is either an array or a collection, each element is sent as a new individual event."——返回值即新事件,这是 14.9 节常见问题的源码出处。
14.7.2 EventListenerMethodProcessor:谁来扫描注解
@EventListener 不是被 BeanPostProcessor 处理的,而是由一个 SmartInitializingSingleton 在所有单例就绪后统一扫描。类路径 spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java,它同时实现了 BeanFactoryPostProcessor、ApplicationContextAware:
public class EventListenerMethodProcessor
implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
protected final Log logger = LogFactory.getLog(getClass());
private @Nullable ConfigurableApplicationContext applicationContext;
private @Nullable ConfigurableListableBeanFactory beanFactory;
private @Nullable List<EventListenerFactory> eventListenerFactories;
private final StandardEvaluationContext originalEvaluationContext;
private final @Nullable EventExpressionEvaluator evaluator;
private final Set<Class<?>> nonAnnotatedClasses = ConcurrentHashMap.newKeySet(64);
nonAnnotatedClasses 是个去重缓存:扫描过、确认没有 @EventListener 方法的类直接记入,下次遇到同类型 bean 跳过——这是高频循环里的短路优化。
为什么要实现 BeanFactoryPostProcessor? 类注释给了答案:"primarily for early retrieval, avoiding AOP checks for this processor bean and its EventListenerFactory delegates"——它在 postProcessBeanFactory(第 96-104 行)里抢先收集所有 EventListenerFactory:
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.originalEvaluationContext.setBeanResolver(new BeanFactoryResolver(this.beanFactory));
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
this.eventListenerFactories = factories;
}
注意 getBeansOfType(..., false, false)——不初始化非单例、也不允许早期引用,只为拿 factory bean 定义。收集到的 factory 列表按 @Order 排序——后面要按这个顺序逐个问"你支持这个方法吗"。BeanFactoryResolver 挂进 SpEL 上下文,让 condition 里能写 @myBean.isReady()。
扫描发生在 afterSingletonsInstantiated()(第 108-150 行):
@Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(beanFactory != null, "No ConfigurableListableBeanFactory set");
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = null;
try {
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
}
if (type != null) {
if (ScopedObject.class.isAssignableFrom(type)) {
// 作用域代理:转向真实 target bean 类型
}
try {
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "': " + ex.getMessage(), ex);
}
}
}
}
}
逐点说明:
- 遍历所有 bean:
getBeanNamesForType(Object.class)等于全量 bean 名(不实例化)。跳过 scoped target(作用域代理的内部目标,避免重复处理)。 - 解析目标类:
AutoProxyUtils.determineTargetClass拿到代理背后的真实类(这很重要——@EventListener注解必须从用户类上读,不能从 CGLIB 代理类上读)。解析不了的(懒加载 bean 的未解析类型)debug 级跳过。 - processBean 是核心,下一节看。
14.7.3 processBean:一个方法对应一个 Factory,产出一个 ApplicationListener
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 (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
}
else {
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 alma) {
alma.init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
}
}
}
逐段解读:
- 三层短路:类已在
nonAnnotatedClasses里 → 直接返回;isCandidateClass判断这个类是否可能带注解(ASM 层面的快速筛选,避免对 JDK 类做反射);isSpringContainerClass排除 Spring 自己的内部 bean(容器自身组件上不会写用户监听器)。 - 方法发现:
MethodIntrospector.selectMethods遍历类的所有方法,AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)——用"合并注解"查找,意味着组合注解(meta-annotated)也能识别,@TransactionalEventListener就是靠这个被认作@EventListener的。 - 空结果入缓存:一个 @EventListener 方法都没有,把类记入
nonAnnotatedClasses,永久短路。 - 方法 × Factory 的双重循环:对每个带注解的方法,按 order 顺序问每个 factory:
supportsMethod(method)返回 true 的那个负责创建——命中即 break,一个方法只会被一个 factory 处理。这就是扩展点:事务监听器用TransactionalEventListenerFactory(supportsMethod检查@TransactionalEventListener)抢走自己的方法,剩下的全部落到兜底的DefaultEventListenerFactory。 - 创建后两件事:
AopUtils.selectInvocableMethod选出可调用方法;若是ApplicationListenerMethodAdapter,调alma.init(context, evaluator)注入 ApplicationContext 与 SpEL 求值器(构造时它们还是 null,事件真正分发时要用);最后context.addApplicationListener(applicationListener)——走 14.3.2 的双写通道登记进多播器。
注意:EventListenerMethodProcessor 本身和 DefaultEventListenerFactory 这两个 bean 是谁注册的?是 AnnotationConfigUtils.registerAnnotationConfigProcessors(spring-context/.../annotation/AnnotationConfigUtils.java 第 192-202 行)在 Java 配置类解析时自动注册的基础设施 bean:
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
bean 名分别是 org.springframework.context.event.internalEventListenerProcessor 与 org.springframework.context.event.internalEventListenerFactory。只要你用了注解驱动(@Configuration/组件扫描),这两个 bean 就自动存在——这就是 @EventListener "开箱即用"的原因。
14.7.4 DefaultEventListenerFactory:兜底工厂
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
private int order = LOWEST_PRECEDENCE;
@Override
public boolean supportsMethod(Method method) {
return true;
}
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodAdapter(beanName, type, method);
}
}
supportsMethod 恒 true——它是"catch-all"(兜底);order 默认 LOWEST_PRECEDENCE,保证其他 factory(如事务 factory 的 order=50)先被询问。创建出来的就是本章后半的主角 ApplicationListenerMethodAdapter。
14.8 源码剖析(六):ApplicationListenerMethodAdapter——注解方法的"监听器化身"
ApplicationListenerMethodAdapter(路径 spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java)实现 GenericApplicationListener,把一个普通 Java 方法包装成多播器认识的监听器。它是整个注解事件体系的运行时核心。
14.8.1 构造期:把方法元数据全部固化
public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
protected final Log logger = LogFactory.getLog(getClass());
private final String beanName;
private final Method method;
private final Method targetMethod;
private final AnnotatedElementKey methodKey;
private final List<ResolvableType> declaredEventTypes;
private final @Nullable String condition;
private final boolean defaultExecution;
private final int order;
private volatile @Nullable String listenerId;
private @Nullable ApplicationContext applicationContext;
private @Nullable EventExpressionEvaluator evaluator;
构造器(第 112-126 行):
public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.defaultExecution = (ann == null || ann.defaultExecution());
this.order = resolveOrder(this.targetMethod);
String id = (ann != null ? ann.id() : "");
this.listenerId = (!id.isEmpty() ? id : null);
}
逐点解读:
- 桥接方法解析:
BridgeMethodResolver.findBridgedMethod——泛型实现里编译器会生成桥接方法,必须拿到用户真正写的那个。 - 最具体方法:
AopUtils.getMostSpecificMethod——目标类是代理时,找到用户类上声明的原始方法。 methodKey:把方法 + 目标类包成AnnotatedElementKey,作为 SpEL 表达式缓存的 key(同一个方法的 condition 表达式只解析一次)。- 读注解:
findMergedAnnotation(targetMethod, EventListener.class)——注意是从 targetMethod 上读,这样@TransactionalEventListener这种组合注解里的@EventListener属性(condition、classes、id)都能被合并读到。
14.8.2 resolveDeclaredEventTypes:方法签名的硬约束
private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) {
int count = (KotlinDetector.isSuspendingFunction(method) ? method.getParameterCount() - 1 :
method.getParameterCount());
if (count > 1) {
throw new IllegalStateException("Maximum one parameter is allowed for event listener method: " + method);
}
if (ann != null) {
Class<?>[] classes = ann.classes();
if (classes.length > 0) {
List<ResolvableType> types = new ArrayList<>(classes.length);
for (Class<?> eventType : classes) {
types.add(ResolvableType.forClass(eventType));
}
return types;
}
}
if (count == 0) {
throw new IllegalStateException(
"Event parameter is mandatory for event listener method: " + method);
}
return Collections.singletonList(ResolvableType.forMethodParameter(method, 0));
}
这就是"方法签名必须单参数"的源码出处:
- 参数个数 > 1 → 直接
IllegalStateException(Kotlin 挂起函数扣掉 Continuation 参数后再数); - 注解上显式声明了
classes→ 每个类包成一个ResolvableType组成列表(一个方法可同时声明监听多种事件); - 没声明 classes 且 0 参 → 抛异常:
@EventListener方法必须有事件参数; - 正常情况:取第一个参数的
ResolvableType(forMethodParameter——它保留泛型信息,如List<OrderEvent>整体作为类型)。
14.8.3 supportsEventType:泛型事件与 Payload 的双层匹配
@Override
public boolean supportsEventType(ResolvableType eventType) {
for (ResolvableType declaredEventType : this.declaredEventTypes) {
if (eventType.hasUnresolvableGenerics() ?
declaredEventType.toClass().isAssignableFrom(eventType.toClass()) :
declaredEventType.isAssignableFrom(eventType)) {
return true;
}
if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
if (declaredEventType.isAssignableFrom(payloadType)) {
return true;
}
if (payloadType.resolve() == null) {
// Always accept such event when the type is erased
return true;
}
}
}
return false;
}
逐段解读:
- 第一轮:直接类型匹配。如果事件类型的泛型不可解析(
hasUnresolvableGenerics,比如从Object.class现场forInstance出来的),退化成原始 Class 的isAssignableFrom;否则做完整泛型级别的isAssignableFrom。 - 第二轮:Payload 拆包匹配。如果收到的是
PayloadApplicationEvent,剥出 payload 的类型,再问"我声明的事件类型能不能接受这个 payload 类型"——这就是@EventListener public void handle(OrderCreatedEvent e)能收到publishEvent(orderCreatedPayload)的原因:事件被包装成PayloadApplicationEvent<OrderCreatedEvent>,剥包后类型对得上。 - 类型擦除兜底:如果 payload 类型解析出来是 null(发布方没给类型、对象又是泛型擦除场景),无条件接受——避免"类型信息丢失导致监听器永远收不到事件"。
14.8.4 processEvent:一次注解监听器的执行五步法
public void processEvent(ApplicationEvent event) {
@Nullable Object[] args = resolveArguments(event);
if (shouldHandle(event, args)) {
Object result = doInvoke(args);
if (result != null) {
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
五步:解析参数 → 条件判断 → 反射调用 → 处理返回值。逐个拆:
第一步 resolveArguments(第 309-326 行):
protected @Nullable Object @Nullable [] resolveArguments(ApplicationEvent event) {
ResolvableType declaredEventType = getResolvableType(event);
if (declaredEventType == null) {
return null;
}
if (this.method.getParameterCount() == 0) {
return new Object[0];
}
Class<?> declaredEventClass = declaredEventType.toClass();
if (!ApplicationEvent.class.isAssignableFrom(declaredEventClass) &&
event instanceof PayloadApplicationEvent<?> payloadEvent) {
Object payload = payloadEvent.getPayload();
if (declaredEventClass.isInstance(payload)) {
return new Object[] {payload};
}
}
return new Object[] {event};
}
getResolvableType(event)找出"这个事件命中了我声明的哪个事件类型"(遍历 declaredEventTypes,支持 payload 拆包);找不到 → 返回 null,后续 shouldHandle 直接 false(双保险)。- 方法 0 参(声明了 classes 但不接参数的情况)→ 空参数数组。
- 关键分支:如果声明的参数类型不是
ApplicationEvent的子类(说明用户写的是"裸载荷类型",如handle(String s)),且事件是PayloadApplicationEvent,就把 payload 拿出来作为方法参数——用户方法看到的是字符串/领域对象本身,不是事件包装。否则把整个 event 对象传进去。
第二步 shouldHandle + SpEL 条件(第 291-301 行):
@Contract("_, null -> false")
private boolean shouldHandle(ApplicationEvent event, @Nullable Object @Nullable [] args) {
if (args == null) {
return false;
}
if (StringUtils.hasText(this.condition)) {
Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
return this.evaluator.condition(
this.condition, event, this.targetMethod, this.methodKey, args);
}
return true;
}
args 为 null 直接不执行;有 condition 才走求值器。求值器 EventExpressionEvaluator(event/EventExpressionEvaluator.java)的 condition 方法:
public boolean condition(String conditionExpression, ApplicationEvent event, Method targetMethod,
AnnotatedElementKey methodKey, @Nullable Object[] args) {
EventExpressionRootObject rootObject = new EventExpressionRootObject(event, args);
EvaluationContext evaluationContext = createEvaluationContext(rootObject, targetMethod, args);
return (Boolean.TRUE.equals(getExpression(this.conditionCache, methodKey, conditionExpression).getValue(
evaluationContext, Boolean.class)));
}
- rootObject 包装事件 + 参数数组——这就是
#root.event、#root.args的来源; MethodBasedEvaluationContext负责把参数按名字/索引暴露成变量(#a0、#p0、参数名);conditionCache按 methodKey + 表达式文本缓存编译后的Expression——同一条 condition 只解析一次 SpEL AST。
第三步 doInvoke 反射调用(第 380-412 行):
protected @Nullable Object doInvoke(@Nullable Object... args) {
Object bean = getTargetBean();
// Detect package-protected NullBean instance through equals(null) check
if (bean.equals(null)) {
return null;
}
try {
ReflectionUtils.makeAccessible(this.method);
if (KotlinDetector.isSuspendingFunction(this.method)) {
return CoroutinesUtils.invokeSuspendingFunction(this.method, bean, args);
}
return this.method.invoke(bean, args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(this.method, bean, args);
throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
}
catch (IllegalAccessException | InaccessibleObjectException ex) {
throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
}
catch (InvocationTargetException ex) {
// Throw underlying exception
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException runtimeException) {
throw runtimeException;
}
else {
String msg = getInvocationErrorMessage(bean, "Failed to invoke event listener method", args);
throw new UndeclaredThrowableException(targetException, msg);
}
}
}
要点:
getTargetBean()=applicationContext.getBean(this.beanName)——注意!每次事件分发都按 bean 名向容器要一次 bean。单例 bean 拿到的永远是同一个实例;但如果 bean 是非单例(作用域 bean),每次拿新实例。- 方法反射调用:
ReflectionUtils.makeAccessible处理 private 方法;Kotlin 挂起函数走CoroutinesUtils。 - 异常解包:
InvocationTargetException里包着监听器方法真正抛出的异常——是 RuntimeException 就原样抛给多播器(于是回到 14.6.3:无 errorHandler 时传播给发布者);受检异常包成UndeclaredThrowableException。 - 代理类型检查:
assertTargetBean在 IllegalArgumentException 时检查方法声明类是否是目标 bean 的类——若 bean 是 JDK 动态代理而方法在用户类上,给出"请用类代理(CGLIB)"的明确报错。
第四步 handleResult:返回值再发布(第 328-371 行):
protected void handleResult(Object result) {
if (REACTIVE_STREAMS_PRESENT && new ReactiveResultHandler().subscribeToPublisher(result)) {
if (logger.isTraceEnabled()) {
logger.trace("Adapted to reactive result: " + result);
}
}
else if (result instanceof CompletionStage<?> completionStage) {
completionStage.whenComplete((event, ex) -> {
if (ex != null) {
handleAsyncError(ex);
}
else if (event != null) {
publishEvents(event);
}
});
}
else {
publishEvents(result);
}
}
private void publishEvents(@Nullable Object result) {
if (result != null && result.getClass().isArray()) {
Object[] events = ObjectUtils.toObjectArray(result);
for (Object event : events) {
publishEvent(event);
}
}
else if (result instanceof Collection<?> events) {
for (Object event : events) {
publishEvent(event);
}
}
else {
publishEvent(result);
}
}
private void publishEvent(@Nullable Object event) {
if (event != null) {
Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
this.applicationContext.publishEvent(event);
}
}
- 普通对象 → 整个作为新事件
applicationContext.publishEvent(result); - 数组 → 每个元素单独发布;
- Collection → 每个元素单独发布;
CompletionStage(如CompletableFuture)→ 异步完成后把结果作为事件发布;- Reactive Streams Publisher → 订阅,每个 onNext 元素都发布。
这就是"@EventListener 方法返回值意外发布新事件"的完整源码链:processEvent 里 result != null → handleResult → publishEvents → applicationContext.publishEvent(...)——新事件会再次走完整的多播流程,可能触发别的监听器,甚至形成"事件链"。写监听器时若不打算产出新事件,必须返回 void——这不是风格建议,是被源码强制的行为。
最后,onApplicationEvent 的入口(第 198-202 行)还有一道闸门:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (this.defaultExecution) {
processEvent(event);
}
}
defaultExecution 来自注解的 defaultExecution() 属性。普通 @EventListener 它是 true,直接 processEvent;而 @TransactionalEventListener 把 fallbackExecution 桥接到这个属性上——为 false 时,这个 onApplicationEvent 变成空操作,真正的处理被推迟到事务同步回调里(下一节)。
14.9 源码剖析(七):事务事件——把监听器挂到事务的时间轴上
业务里最常见的事件诉求是:"数据库事务提交成功之后,再发 MQ / 调外部接口"。@TransactionalEventListener 就是这个诉求的官方答案。它在 7.0.9 中位于 spring-tx/src/main/java/org/springframework/transaction/event/,是注解事件体系的一个特化分支。
14.9.1 注解与 phase 枚举
@TransactionalEventListener(spring-tx/.../event/TransactionalEventListener.java)的本质是一个被 @EventListener 元注解标注的组合注解:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {
TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] value() default {};
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] classes() default {};
@AliasFor(annotation = EventListener.class, attribute = "condition")
String condition() default "";
/**
* Whether the event should be handled if no transaction is running.
*/
@AliasFor(annotation = EventListener.class, attribute = "defaultExecution")
boolean fallbackExecution() default false;
@AliasFor(annotation = EventListener.class, attribute = "id")
String id() default "";
}
三个关键设计:
@EventListener元注解:EventListenerMethodProcessor的findMergedAnnotation(method, EventListener.class)能同时认出它——所以事务监听器走的是与普通监听器完全相同的扫描通道,只是被TransactionalEventListenerFactory抢走处理权。phase默认AFTER_COMMIT:事务提交后才处理。fallbackExecution默认 false,且@AliasFor桥接到@EventListener.defaultExecution:这就是 14.8.4 末尾那道if (this.defaultExecution)闸门的语义来源——无事务时,默认不执行;设为 true 才降级为普通监听器行为。
TransactionPhase 枚举(TransactionPhase.java)四个值,javadoc 里藏着一个版本级事实:
public enum TransactionPhase {
BEFORE_COMMIT,
/**
* Note: This is a specialization of {@link #AFTER_COMPLETION} and therefore
* executes in the same sequence of events as {@code AFTER_COMPLETION}
* (and not in {@link TransactionSynchronization#afterCommit()}).
*/
AFTER_COMMIT,
AFTER_ROLLBACK,
AFTER_COMPLETION
}
注意 AFTER_COMMIT 明确不是挂在 afterCommit() 回调上,而是挂在 afterCompletion(int status) 上。这与很多老资料的描述不同,源码见下。
14.9.2 TransactionalEventListenerFactory 与它的受限子类
事务侧的 EventListenerFactory 实现(spring-tx/.../event/TransactionalEventListenerFactory.java):
public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered {
private int order = 50;
@Override
public int getOrder() {
return this.order;
}
@Override
public boolean supportsMethod(Method method) {
return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);
}
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new TransactionalApplicationListenerMethodAdapter(beanName, type, method);
}
}
order=50,远高于默认工厂的 LOWEST_PRECEDENCE——它先抢。supportsMethod 检查方法上有没有 @TransactionalEventListener。
实际注册到容器里的不是它本身,而是 RestrictedTransactionalEventListenerFactory(spring-tx/.../annotation/RestrictedTransactionalEventListenerFactory.java,6.1 引入),由 AbstractTransactionManagementConfiguration.transactionalEventListenerFactory() 方法(spring-tx/.../annotation/AbstractTransactionManagementConfiguration.java 第 96-97 行)注册:
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new RestrictedTransactionalEventListenerFactory();
}
受限子类在创建适配器后多做一步合法性检查(第 41-58 行):
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
TransactionalApplicationListenerMethodAdapter adapter =
new TransactionalApplicationListenerMethodAdapter(beanName, type, method);
if (adapter.getTransactionPhase() != TransactionPhase.BEFORE_COMMIT) {
Transactional txAnn = AnnotatedElementUtils.findMergedAnnotation(method, Transactional.class);
if (txAnn == null) {
txAnn = AnnotatedElementUtils.findMergedAnnotation(type, Transactional.class);
}
if (txAnn != null) {
Propagation propagation = txAnn.propagation();
if (propagation != Propagation.REQUIRES_NEW && propagation != Propagation.NOT_SUPPORTED) {
throw new IllegalStateException("@TransactionalEventListener method must not be annotated with " +
"@Transactional unless when declared as REQUIRES_NEW or NOT_SUPPORTED: " + method);
}
}
}
return adapter;
}
为什么 AFTER_COMMIT 类阶段的监听器方法不能加普通 @Transactional? 因为 AFTER_COMMIT 时外层事务已经结束——方法上再来一个 PROPAGATION_REQUIRED 事务会立即新开一个独立事务,与"主事务成功后再做事"的语义脱节,极易造成数据不一致。6.1 起框架直接在启动期抛出 IllegalStateException 禁止这种配置(除非传播行为本身就是 REQUIRES_NEW 或 NOT_SUPPORTED,那是开发者显式有意为之)。这是 7.0.9 中一个"fail-fast"的版本差异点,老资料不会提到。
14.9.3 TransactionalApplicationListenerMethodAdapter:把执行推迟到事务回调
public class TransactionalApplicationListenerMethodAdapter extends ApplicationListenerMethodAdapter
implements TransactionalApplicationListener<ApplicationEvent> {
private final TransactionPhase transactionPhase;
private final List<SynchronizationCallback> callbacks = new CopyOnWriteArrayList<>();
public TransactionalApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
super(beanName, targetClass, method);
TransactionalEventListener eventAnn =
AnnotatedElementUtils.findMergedAnnotation(getTargetMethod(), TransactionalEventListener.class);
if (eventAnn == null) {
throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method);
}
this.transactionPhase = eventAnn.phase();
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (TransactionalApplicationListenerSynchronization.register(event, this, this.callbacks)) {
if (logger.isDebugEnabled()) {
logger.debug("Registered transaction synchronization for " + event);
}
}
else if (isDefaultExecution()) {
if (getTransactionPhase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
}
processEvent(event);
}
else {
// No transactional event execution at all
if (logger.isDebugEnabled()) {
logger.debug("No transaction is active - skipping " + event);
}
}
}
}
这是事务事件的总开关,三分支语义:
register返回 true:当前线程绑定着活跃事务——同步已注册成功,事件的处理被推迟到事务的对应阶段,本次 onApplicationEvent 立即返回(什么都没做)。- register 返回 false 且
isDefaultExecution()为 true(即fallbackExecution = true):无事务也立即processEvent(event)降级执行;如果 phase 是 AFTER_ROLLBACK 还打一条 warn——"在没有回滚的世界里按回滚语义处理了事件",提醒开发者这不是预期行为。 - 否则:静默跳过(debug 日志)。这就是"无事务时 @TransactionalEventListener 默认不触发"的源码出处。
14.9.4 TransactionalApplicationListenerSynchronization:真正的事务挂接点
注册动作在 TransactionalApplicationListenerSynchronization.register(spring-tx/.../event/TransactionalApplicationListenerSynchronization.java 第 75-94 行):
public static <E extends ApplicationEvent> boolean register(
E event, TransactionalApplicationListener<E> listener,
List<TransactionalApplicationListener.SynchronizationCallback> callbacks) {
if (org.springframework.transaction.support.TransactionSynchronizationManager.isSynchronizationActive() &&
org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive()) {
org.springframework.transaction.support.TransactionSynchronizationManager.registerSynchronization(
new PlatformSynchronization<>(event, listener, callbacks));
return true;
}
else if (event.getSource() instanceof TransactionContext txContext) {
org.springframework.transaction.reactive.TransactionSynchronizationManager rtsm =
new org.springframework.transaction.reactive.TransactionSynchronizationManager(txContext);
if (rtsm.isSynchronizationActive() && rtsm.isActualTransactionActive()) {
rtsm.registerSynchronization(new ReactiveSynchronization<>(event, listener, callbacks));
return true;
}
}
return false;
}
两道闸门判断"当前是否真有事务":isSynchronizationActive()(事务同步已开启)且 isActualTransactionActive()(存在真实事务,而非空的同步上下文)。通过后,把一个 PlatformSynchronization 注册进 TransactionSynchronizationManager——事务管理器在提交/回滚时会回调它。第二分支处理响应式事务(Reactor TransactionContext 作为事件 source 的场景)。
回调本体 PlatformSynchronization(第 97-127 行):
private static class PlatformSynchronization<AE extends ApplicationEvent>
extends TransactionalApplicationListenerSynchronization<AE>
implements org.springframework.transaction.support.TransactionSynchronization {
@Override
public void beforeCommit(boolean readOnly) {
if (getTransactionPhase() == TransactionPhase.BEFORE_COMMIT) {
processEventWithCallbacks();
}
}
@Override
public void afterCompletion(int status) {
TransactionPhase phase = getTransactionPhase();
if (phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {
processEventWithCallbacks();
}
else if (phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
processEventWithCallbacks();
}
else if (phase == TransactionPhase.AFTER_COMPLETION) {
processEventWithCallbacks();
}
}
}
逐阶段对应:
| phase | 挂接的 TransactionSynchronization 回调 | 触发条件 |
|---|---|---|
BEFORE_COMMIT | beforeCommit(readOnly) | 事务提交之前(还可抛异常阻止提交) |
AFTER_COMMIT | afterCompletion(status) | 且 status == STATUS_COMMITTED(提交成功) |
AFTER_ROLLBACK | afterCompletion(status) | 且 status == STATUS_ROLLED_BACK(回滚) |
AFTER_COMPLETION | afterCompletion(status) | 无论提交还是回滚都执行 |
再次强调版本事实:AFTER_COMMIT 不在 afterCommit() 里执行。从 Spring 5.3 起,所有"事务结束后"的 phase 统一收敛到 afterCompletion(int),用 status 参数区分提交/回滚。afterCommit() 与 afterCompletion() 的时序差异(afterCommit 在事务资源完全清理之前调用)在 5.3 前曾带来"在 AFTER_COMMIT 里还能写库"的意外行为,统一到 afterCompletion 后语义更干净。
processEventWithCallbacks(第 62-72 行)则是实际执行点:
public void processEventWithCallbacks() {
this.callbacks.forEach(callback -> callback.preProcessEvent(this.event));
try {
this.listener.processEvent(this.event);
}
catch (RuntimeException | Error ex) {
this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, ex));
throw ex;
}
this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, null));
}
processEvent(event) 在 TransactionalApplicationListenerMethodAdapter 中是从父类 ApplicationListenerMethodAdapter 继承来的——也就是说,真正的 SpEL 条件求值、反射调用、返回值再发布,全部复用普通监听器那套逻辑,事务侧只负责"在正确的事务时点调用它"。
最后补一个接口级事实:TransactionalApplicationListener.supportsAsyncExecution() 硬编码返回 false(TransactionalApplicationListener.java 第 70-73 行):
/**
* Transaction-synchronized listeners do not support asynchronous execution,
* only their target listener ({@link #processEvent}) potentially does.
*/
@Override
default boolean supportsAsyncExecution() {
return false;
}
这就是 14.6.2 两道闸门中第二道闸门的语义闭环:哪怕用户给多播器配了全局异步 executor,事务事件监听器的 onApplicationEvent(即"注册事务同步"这个动作)也必须在发布线程同步执行——否则注册动作根本找不到 TransactionSynchronizationManager 里的事务。而同步回调真正执行 processEvent 的时点,已经是事务管理器的线程上了。
14.10 源码剖析(八):泛型事件匹配的边界
14.10.1 ResolvableType.isAssignableFrom 在事件匹配中的位置
整套事件匹配最终都收敛到 ResolvableType.isAssignableFrom(ResolvableType) 这一个判定上,出现的位置有三处:
GenericApplicationListenerAdapter.supportsEventType:this.declaredEventType.isAssignableFrom(eventType)——裸ApplicationListener<X>与实际事件类型的匹配;AbstractApplicationEventMulticaster.supportsEvent(Class, ResolvableType):declaredEventType.isAssignableFrom(eventType)——按 bean 名预检时用;ApplicationListenerMethodAdapter.supportsEventType:declaredEventType.isAssignableFrom(eventType)——注解监听器的匹配,含 Payload 拆包。
ResolvableType(spring-core)对泛型的判定规则与 Java 类型系统一致:List<String> 不能接受 List<Integer>(泛型不型变),但 List 原始类型可以接受一切 List。理解了这一点,下面两个经典边界就有答案了。
14.10.2 边界一:泛型事件类可以,泛型监听器参数受限
可以的写法(事件类本身带泛型):
public class OrderEvent<T> extends ApplicationEvent { ... }
@EventListener
public void handle(OrderEvent<OrderCreated> e) { ... }
发布端如果 new OrderEvent<>(this, payload) 直接构造,运行时 ResolvableType.forInstance(event) 拿到的是 OrderEvent 的原始类(泛型参数被擦除)——此时 ApplicationListenerMethodAdapter.supportsEventType 里 eventType.hasUnresolvableGenerics() 为 true,退化为 declaredEventType.toClass().isAssignableFrom(eventType.toClass()),即只按原始类匹配,泛型参数被忽略。要让泛型参数精确匹配,事件类必须实现 ResolvableTypeProvider(像 PayloadApplicationEvent 那样),在运行时显式回报泛型签名。
受限的写法(泛型监听器):
@EventListener
public void handle(List<OrderEvent> events) { ... }
这种写法的行为取决于发布端是否给出 payload 的 ResolvableType:
publishEvent(List)普通调用:PayloadApplicationEvent构造时ResolvableType.forInstance(payload)只能推断出ArrayList(运行时类),监听器声明的List<OrderEvent>与ArrayList做isAssignableFrom时,泛型部分对不上——不匹配;- 发布端显式构造
new PayloadApplicationEvent<>(this, list, ResolvableType.forClassWithGenerics(List.class, OrderEvent.class)):类型信息完整,匹配成功。
源码佐证就在 ApplicationListenerMethodAdapter.supportsEventType 的 Payload 分支:
if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
if (declaredEventType.isAssignableFrom(payloadType)) {
return true;
}
if (payloadType.resolve() == null) {
// Always accept such event when the type is erased
return true;
}
}
payloadType.resolve() == null 时无条件接受——这是"类型擦除场景下宁可错收不可漏收"的宽容策略。
14.10.3 边界二:lambda 监听器的泛型丢失
14.6.4 节讲过:lambda ApplicationListener<OrderCreatedEvent> e -> ... 的 class 是合成类,GenericApplicationListenerAdapter.resolveDeclaredEventType 反射不出 OrderCreatedEvent,declaredEventType 为 null——supportsEventType 永远返回 true(放行所有事件),真正的类型过滤退化为运行时 onApplicationEvent 里的 ClassCastException 智能容错。
实践结论:lambda 监听器永远不要写复杂泛型签名;要精确按事件类型过滤,就用 @EventListener 方法(编译后类保留泛型签名,forMethodParameter 拿得到)或 GenericApplicationListener.forEventType(Class, Consumer) 显式声明事件类。
14.11 运行机制:把源码串成时序
14.11.1 一次同步发布的完整调用栈
以用户在 service 方法里 publisher.publishEvent(new OrderCreatedEvent(order))、容器已 refresh 完成、无事务、无 taskExecutor 为例:
业务方法
└─ ApplicationContext.publishEvent(OrderCreatedEvent) [AbstractApplicationContext:414]
├─ event 已是 ApplicationEvent,直接使用
├─ eventType = ResolvableType.forInstance(event)(OrderCreatedEvent)
├─ earlyApplicationEvents == null(窗口已关)
├─ applicationEventMulticaster.multicastEvent(event, eventType) [SimpleApplicationEventMulticaster:137]
│ ├─ getApplicationListeners(event, type) [AbstractApplicationEventMulticaster:188]
│ │ ├─ ListenerCacheKey(OrderCreatedEvent, source=appContext)
│ │ ├─ retrieverCache 命中(稳态)→ CachedListenerRetriever.getApplicationListeners()
│ │ │ ├─ 实例子集直接取出
│ │ │ └─ bean 名子集 → beanFactory.getBean(name) 现取单例
│ │ └─ AnnotationAwareOrderComparator.sort(已排好序)
│ ├─ executor == null → 同步分支
│ └─ for each listener:
│ invokeListener(listener, event) [:162]
│ ├─ errorHandler == null → doInvokeListener [:178]
│ │ └─ listener.onApplicationEvent(event)
│ │ (注解监听器 → ApplicationListenerMethodAdapter.onApplicationEvent
│ │ → defaultExecution=true → processEvent
│ │ → resolveArguments → shouldHandle(无 condition 直接 true)
│ │ → doInvoke → getBean(beanName) → method.invoke(bean, args)
│ │ → result != null → handleResult → publishEvent(...) 再次多播)
│ └─ 监听器抛 RuntimeException → 异常向上穿透 invokeListener → 穿透 for 循环
│ → 传播回 multicastEvent → 传播回 publishEvent → 传播回业务方法
└─ parent != null → 父容器 publishEvent(event, typeHint) 递归冒泡
这条栈里最需要记住的三个性质:
- 同步:业务线程一直阻塞到所有监听器执行完(无异步 executor 时);
- 故障全开:任何一个监听器抛 RuntimeException,后续监听器不再执行,异常直接炸回业务方法;
- 冒泡:同一事件沿父子容器链向上广播。
14.11.2 异步发布的两种形态与行为差异
形态 A:多播器级异步(setTaskExecutor(pool))。14.6.2 的分支把每个"同意异步"的监听器包成 executor.execute(() -> invokeListener(...))。差异点:
- 发布者线程立即返回,不等待监听器;
- 线程上下文丢失:事务同步、MDC、ThreadLocal 用户上下文全部不在新线程里——这就是为什么
TransactionalApplicationListener.supportsAsyncExecution()必须返回 false; - 异常不再传播给发布者(异常被丢进线程池,配合 errorHandler 才能被观察到);
- RejectedExecutionException 时同步兜底执行该监听器。
形态 B:监听器级 @Async。监听器方法上加 @Async,由 AOP 代理在调用 onApplicationEvent 时切到异步线程。它与形态 A 可叠加:多播器同步调到代理的 onApplicationEvent,代理再异步切线程。注意 @Async 监听器的异常由 AsyncUncaughtExceptionHandler 处理,与多播器 errorHandler 是两套体系。
14.11.3 早期事件的生命周期状态机
把 earlyApplicationEvents 的 null/非 null 当作状态变量,整个 refresh 窗口里事件的去向是:
prepareRefresh()
earlyApplicationEvents = new LinkedHashSet<>() → 状态:COLLECTING
│
├─ 此期间任何 publishEvent → 只进集合,不多播
│ (包括 BeanFactoryPostProcessor、BeanPostProcessor 注册过程中的事件)
│
initApplicationEventMulticaster() → 多播器就绪
registerListeners()
├─ 静态监听器实例入账
├─ 监听器 bean 名入账
├─ earlyEventsToProcess = 集合; earlyApplicationEvents = null → 状态:REPLAYING→LIVE
└─ 按插入顺序重放每个早期事件 multicastEvent(earlyEvent)
(重放期间新事件因字段为 null 而立即多播,不会二次缓冲)
│
finishBeanFactoryInitialization()
└─ 所有单例就绪 → EventListenerMethodProcessor.afterSingletonsInstantiated()
→ @EventListener 方法才包装成监听器入账(此时早期事件早已重放完毕)
│
finishRefresh() → publishEvent(ContextRefreshedEvent) → 状态:LIVE
由此推出一个高频排障结论:在 BeanFactoryPostProcessor 阶段发布的事件,所有 @EventListener 方法都收不到——重放时注解监听器还没被创建;能收到的只有静态注册的 ApplicationListener 实例与按 bean 名登记(且此刻被 getBean 提前实例化)的监听器 bean。反之,ContextRefreshedEvent 发布时,包括注解监听器在内的全部监听器都已就位。
14.11.4 事件的注册-失效周期
缓存的失效时机只有一个:AbstractApplicationEventMulticaster 的任何注册变更方法(add/remove/clear)都会 retrieverCache.clear()。结合注册时点:
- refresh 期间注册密集(registerListeners 批量加 bean 名、Detector 逐个捕获单例、Processor 收尾加注解适配器),缓存反复清空——这是预期内的,启动期不追求匹配性能;
- 容器就绪后,注册变更变成低频事件(用户运行期动态加监听器、bean 销毁摘除),每次清空缓存后第一次发布某类型事件会重新过滤+排序,之后稳态命中。
数据结构上的线程安全:retrieverCache 本身是 ConcurrentHashMap 并发读;CachedListenerRetriever 的两个监听器集合字段用 volatile 发布;注册表快照在 synchronized 块内复制。整个设计是"读写分离 + 最终一致",没有一把大锁贯穿分发路径。
14.12 扩展用法与最佳实践
14.12.1 三种自定义事件的姿势与取舍
姿势一:继承 ApplicationEvent(经典写法)。
public class OrderCreatedEvent extends ApplicationEvent {
private final Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() { return this.order; }
}
- 优点:类型语义清晰,
ApplicationListener<OrderCreatedEvent>精确匹配;事件自带 timestamp;框架事件(ContextRefreshedEvent 等)都是这个套路。 - 缺点:每个事件一个类。
姿势二:publishEvent(Object) + PayloadApplicationEvent 自动包装。
applicationEventPublisher.publishEvent(order); // order 是个普通 POJO
@EventListener
public void handle(Order order) { ... } // 直接接 POJO
- 原理见 14.3.1:普通对象被包成
PayloadApplicationEvent,监听器参数声明 POJO 类型时由resolveArguments自动剥包。 - 适用:领域事件快速原型、不想建事件类的场景。代价:丢失"事件类型"这一层语义(所有 POJO 共用
PayloadApplicationEvent壳,靠 payload 类型区分),且事件源统一是容器。
姿势三:实现 ResolvableTypeProvider 的泛型事件。要让监听器写 @EventListener public void handle(GenericDomainEvent<OrderCreated> e) 这种泛型签名并精确匹配,事件类必须自己实现 ResolvableTypeProvider(参考 PayloadApplicationEvent.getResolvableType() 的写法),在运行时回报泛型签名。否则泛型参数被擦除,框架只能按原始类匹配。
14.12.2 事件驱动架构(领域事件)的标准用法
DDD 风格的领域事件在 Spring 里的落地套路:
- 聚合根/领域服务在业务方法内
applicationEventPublisher.publishEvent(new OrderPaidEvent(orderId))——事件是"事实",载荷只放 ID 与必要快照; - 应用服务层用
@TransactionalEventListener(phase = AFTER_COMMIT)监听,事务提交成功后再发 MQ、调外部 HTTP、写搜索索引; - 需要"事件后再发事件"的,利用注解监听器返回值机制(
return new SendMqCommand(...))形成处理链——但要克制,链式事件超过两层就应改回显式编排; - 跨限界上下文的集成,不要用同步事件直接调——事件监听器里同步调外部服务会把"发布者的事务"拖垮(回忆 14.11.1:同步监听器抛异常会炸回发布者)。
14.12.3 @TransactionalEventListener 的标准姿势
"事务成功后发消息"是官方推荐的场景,源码依据在 14.9.4:
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
publisher.publishEvent(new OrderCreatedEvent(order.getId()));
}
}
@Component
public class OrderNotificationListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Async // 需要异步发 MQ 时自己加 @Async
public void on(OrderCreatedEvent event) {
mqClient.send("order.created", event.getOrderId());
}
}
要点:
- 发布必须在事务方法内——
register判断的是"发布那一刻当前线程有没有活跃事务"。在事务方法外发布,fallbackExecution=false(默认)时监听器直接不执行; - AFTER_COMMIT 阶段操作数据库要谨慎:事务已提交,但连接资源可能还在(javadoc 警告"any data access code triggered at this point will still participate in the original transaction, but changes will not be committed")。该阶段写库等于白写——要写新数据请开 REQUIRES_NEW 或用 AFTER_COMMIT 之后的异步线程;
- 需要"没事务也照常执行"的后台任务场景,显式
fallbackExecution = true; - 需要"回滚时补偿"的场景用
AFTER_ROLLBACK,注意此时主事务数据已回滚,不要指望读到刚写的数据。
14.12.4 替换与增强多播器
两个官方扩展点:
- 自定义多播器 bean:定义一个 bean 名字正好是
applicationEventMulticaster(类型ApplicationEventMulticaster),initApplicationEventMulticaster会优先用它——可以做"事件总线外发到 MQ""按租户路由"等定制; - 运行期改默认多播器行为:
getBean("applicationEventMulticaster", SimpleApplicationEventMulticaster.class)后setTaskExecutor(...)/setErrorHandler(...)。推荐 errorHandler 用TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER(打日志吞掉)或LOG_AND_PROPAGATE_ERROR_HANDLER——否则同步发布下一个监听器的异常会中断整条链。
14.12.5 自定义 EventListenerFactory
@EventListener 的处理逻辑可以整体替换:实现 EventListenerFactory(supportsMethod + createApplicationListener),注册成容器 bean。EventListenerMethodProcessor.postProcessBeanFactory 会自动收集所有 factory 并按 order 排序——TransactionalEventListenerFactory(order=50)就是范本:它拦截带 @TransactionalEventListener 的方法,产出事务版适配器。自定义 factory 的典型用途:给某类注解方法统一加日志/监控埋点、把方法包装成带特殊上下文的适配器。
14.12.6 性能与线程安全注意点
- 监听器数量与匹配开销:稳态下每次发布是
retrieverCache命中后遍历已排序列表(O(n),n 为匹配该类型的监听器数)。真正的代价只发生在缓存 miss(注册变更后首次):遍历全部注册表 + 泛型匹配 + 排序。因此避免在运行期频繁 add/remove 监听器——每次变更都清空整张缓存。 - bean 名监听器的现取:
CachedListenerRetriever.getApplicationListeners()对缓存里只存名字的非单例监听器,每次都beanFactory.getBean(name)——高频事件 + 原型监听器 = 高频创建 bean,应尽量用单例。 - SpEL condition 开销:表达式 AST 有缓存(
conditionCache),但每次求值仍要构造MethodBasedEvaluationContext——高频事件里谨慎写复杂 condition,能不写就不写(类型过滤已经够精确)。 - 线程安全:事件发布本身在同步路径上是线程安全的(缓存并发读、注册表快照);但监听器实现自己要保证线程安全——异步执行时多个事件可能并发进入同一个单例监听器方法。
- 排序稳定性:
AnnotationAwareOrderComparator.sort对 order 相同的监听器保持注册顺序(LinkedHashSet 语义),所以"同 order 谁先谁后"是可预测的,但不要依赖它做业务编排——有依赖关系的监听器请显式分事件。
14.13 常见问题与坑
14.13.1 同步监听器抛异常,发布者的事务回滚了——是 bug 吗?
不是。源码链:doInvokeListener 里 listener.onApplicationEvent(event) 抛出 RuntimeException → invokeListener(无 errorHandler 分支)原样抛出 → 逃出 multicastEvent 的 for 循环(排在后面的监听器也不再执行)→ 沿 publishEvent 传回业务方法 → @Transactional 代理看到异常,回滚。
这是 SimpleApplicationEventMulticaster javadoc 明说的默认语义:默认不设 errorHandler 时,"监听器异常会中断当前多播,并传播给事件发布者"。
解法按场景选:
- 监听器内部自己 try-catch 消化异常(边界清晰的做法);
- 给多播器
setErrorHandler为TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER(全局吞异常,注意别掩盖问题); - 把该监听器改成
@Async(异常进异步体系,不影响发布者)——但代价是丢了同步事务上下文。
14.13.2 异步监听器的异常为什么"消失"了?
两种异步路径,两套异常体系:
- 多播器 taskExecutor 异步:
executor.execute(() -> invokeListener(...))抛出的异常进线程池。无 errorHandler 时,invokeListener里的 throw 发生在 worker 线程,发布者线程早就返回了——表现为"异常打在控制台日志里,业务毫无感知"。 - @Async 监听器:异常由
AsyncUncaughtExceptionHandler处理,同样与发布者无关。
排障时先确认监听器走的是哪条异步路径,再去对应体系里找异常日志。
14.13.3 @EventListener 方法有返回值,结果被当成新事件发出去了
源码出处是 ApplicationListenerMethodAdapter.processEvent:result != null → handleResult(result) → publishEvents(result) → applicationContext.publishEvent(event)。数组/集合逐元素发布,CompletionStage 异步完成后发布。
最隐蔽的翻车场景:监听器方法本来只想 return "处理完成" 打个日志,结果这个字符串被包成 PayloadApplicationEvent<String> 再次多播——另一个监听 String 载荷的监听器(或一个万能的 @EventListener public void on(Object o))被意外触发,形成诡异循环。铁律:不想产出新事件就必须返回 void。注解 javadoc 专门写了提醒:异步事件监听器方法不能靠返回值发布后续事件,要发请注入 ApplicationEventPublisher 手动发。
14.13.4 @TransactionalEventListener 在没有事务时静默不执行
源码出处:TransactionalApplicationListenerMethodAdapter.onApplicationEvent——TransactionalApplicationListenerSynchronization.register 在 TransactionSynchronizationManager.isActualTransactionActive() 为 false 时返回 false,fallbackExecution(默认 false)又把 defaultExecution 桥成 false → 进入"debug 级 skip"分支。
典型翻车:单元测试里直接调 service 方法发布事件(测试方法上没 @Transactional),监听器死活不跑,误以为是类型匹配问题。解法:无事务场景显式 fallbackExecution = true,或在测试里挂上事务。
14.13.5 子/父容器中的事件隔离与冒泡
规则(源码见 14.3.1 第四段):子容器发布的事件会冒泡到父容器,父容器的监听器收得到;父容器发布的事件不会通知子容器。且冒泡时传的是原始 event 与已解析 typeHint,沿 parent instanceof AbstractApplicationContext 递归向上。
Web 场景高频坑:Spring Boot 父子上下文(root + servlet)里,把"只该给 web 层用"的监听器注册进了父容器,结果 root 上下文发布的所有事件都触发了它;或者反过来,监听器注册在了子上下文,却以为 root 发布的事件也能收到。排查时先问:这个监听器 bean 在哪个上下文里?
14.13.6 循环依赖/启动期发布事件,监听器没收到
两个时点问题:
- refresh 完成前发布:事件进
earlyApplicationEvents队列,在registerListeners()时重放(14.11.3 状态机)。但重放发生在finishBeanFactoryInitialization之前——@EventListener方法由EventListenerMethodProcessor.afterSingletonsInstantiated在所有单例就绪后才登记,启动期事件它们注定收不到。需要在启动早期干活的逻辑,请实现ApplicationListener<ContextRefreshedEvent>接口或用SmartInitializingSingleton,而不是@EventListener。 - bean 创建过程中(构造/@PostConstruct)发布事件:此时该 bean 自己还没初始化完,如果事件的监听器又依赖这个 bean,就是循环依赖——Spring 会提前暴露引用,但事件同步多播的调用栈可能让你拿到一个半成品 bean。规范做法:发布事件放在业务方法里,不要放在 bean 生命周期回调里。
14.13.7 AFTER_COMMIT 里写数据库为什么"不生效"
@TransactionalEventListener(AFTER_COMMIT) 的监听器跑在 TransactionSynchronization.afterCompletion 回调里——主事务已提交、连接虽可能还挂着但事务边界已结束。此时监听器方法里 xxxRepository.save(...) 的变更:要么参与不到任何事务(自动提交模式下语句各自提交,看似正常),要么参与到"已结束的事务资源"中(注解 javadoc 警告的坑:语句执行了但不会被提交)。需要在事件监听器里写库,方法上自己加 @Transactional(propagation = REQUIRES_NEW) 开新事务——且记住 RestrictedTransactionalEventListenerFactory 的限制:phase 不是 BEFORE_COMMIT 时,监听器方法上的 @Transactional 只允许 REQUIRES_NEW/NOT_SUPPORTED,否则启动期直接抛 IllegalStateException。
14.13.8 一个监听器被执行了两次
按源码,重复执行只有两个来源:
- 代理与裸目标同时登记:
AbstractApplicationEventMulticaster.addApplicationListener与retrieveApplicationListeners里专门有AopProxyUtils.getSingletonTarget去重逻辑防这个;如果你的 bean 被手动addApplicationListener(代理)又被 Detector 捕获了裸目标,框架会用代理替换裸目标。仍重复时,检查是不是同一个监听器类既被组件扫描注册、又被你手动addApplicationListener了一次。 - 父子容器双注册:bean 在父子两个上下文里都存在,事件冒泡导致两边各执行一次。
14.13.9 为什么监听器的顺序不像我想的那样
排序发生在两处:retrieveApplicationListeners 末尾与 CachedListenerRetriever.getApplicationListeners() 末尾,都是 AnnotationAwareOrderComparator.sort。规则:@Order 值小的先执行;裸 ApplicationListener 没实现 Ordered/没注解 = LOWEST_PRECEDENCE(排最后);SmartApplicationListener.getOrder() 默认也是 LOWEST_PRECEDENCE;ApplicationListenerMethodAdapter 的 order 来自方法上的 @Order。注意:排序只对"匹配同一事件的监听器集合"生效——不同事件类型的监听器之间没有顺序概念。
line1
line2
14.14 本章小结
本章沿着四个问题把 Spring 7.0.9 事件机制的源码链路走了一遍:
- 抽象层:ApplicationEvent 与 PayloadApplicationEvent 承载事件,三层监听接口把类型判定从 Class 升级到 ResolvableType。
- 发布层:AbstractApplicationContext.publishEvent 完成包装、早期事件入队、立即多播、父容器冒泡。
- 装配层:initApplicationEventMulticaster、registerListeners、ApplicationListenerDetector、EventListenerMethodProcessor 串成装配链。
- 分发层:ListenerCacheKey 缓存预过滤结果,supportsEvent 三层判定,AnnotationAwareOrderComparator 排序。
- 注解层:EventListenerFactory 策略链到 ApplicationListenerMethodAdapter,SpEL 求值、反射调用、返回值再发布。
- 事务层:TransactionalEventListener 桥接 defaultExecution,发布点注册事务同步并推迟到事务回调执行。
本章关键类清单
| 类名 | 模块与相对路径 | 职责一句话 |
|---|---|---|
| ApplicationEvent | spring-context/src/main/java/org/springframework/context/ApplicationEvent.java | 事件抽象基类:source + 不可变 timestamp |
| PayloadApplicationEvent | spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java | 任意对象包装为事件,保留泛型 |
| ApplicationEventPublisher | spring-context/src/main/java/org/springframework/context/ApplicationEventPublisher.java | 发布门面接口 |
| ApplicationListener | spring-context/src/main/java/org/springframework/context/ApplicationListener.java | 监听根接口 onApplicationEvent |
| SmartApplicationListener | spring-context/src/main/java/org/springframework/context/event/SmartApplicationListener.java | 暴露事件/源类型判定与 order 元数据 |
| GenericApplicationListener | spring-context/src/main/java/org/springframework/context/event/GenericApplicationListener.java | 基于 ResolvableType 的事件类型判定接口 |
| GenericApplicationListenerAdapter | spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java | 包装裸监听器,缓存 declaredEventType |
| ApplicationEventMulticaster | spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java | 多播器接口:注册管理 + multicastEvent |
| AbstractApplicationEventMulticaster | spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java | 注册表 + retrieverCache + 三层过滤排序 |
| SimpleApplicationEventMulticaster | spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java | 默认多播:同步/异步、errorHandler、CCE 容错 |
| AbstractApplicationContext | spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java | publishEvent 全流程与容器事件装配 |
| ApplicationListenerDetector | spring-context/src/main/java/org/springframework/context/support/ApplicationListenerDetector.java | BeanPostProcessor 兜底捕获单例监听器 |
| EventListener | spring-context/src/main/java/org/springframework/context/event/EventListener.java | 注解:classes/condition/defaultExecution/id |
| EventListenerMethodProcessor | spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java | 扫描 @EventListener 方法并注册适配器 |
| EventListenerFactory | spring-context/src/main/java/org/springframework/context/event/EventListenerFactory.java | 策略接口:supportsMethod + createApplicationListener |
| DefaultEventListenerFactory | spring-context/src/main/java/org/springframework/context/event/DefaultEventListenerFactory.java | 兜底工厂,产出 ApplicationListenerMethodAdapter |
| ApplicationListenerMethodAdapter | spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java | 注解方法的监听器化身,返回值再发布 |
| EventExpressionEvaluator | spring-context/src/main/java/org/springframework/context/event/EventExpressionEvaluator.java | SpEL condition 求值与表达式缓存 |
| TransactionalEventListener | spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListener.java | 组合注解,phase 默认 AFTER_COMMIT |
| TransactionPhase | spring-tx/src/main/java/org/springframework/transaction/event/TransactionPhase.java | BEFORE_COMMIT/AFTER_COMMIT/AFTER_ROLLBACK/AFTER_COMPLETION |
| TransactionalEventListenerFactory | spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListenerFactory.java | 识别事务监听器方法,order=50 |
| RestrictedTransactionalEventListenerFactory | spring-tx/src/main/java/org/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.java | 拦截监听器方法上的违规 @Transactional 配置 |
| TransactionalApplicationListenerMethodAdapter | spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapter.java | 注册事务同步,决定降级或推迟执行 |
| TransactionalApplicationListenerSynchronization | spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListenerSynchronization.java | beforeCommit/afterCompletion(status) 分发 |
| TransactionalApplicationListener | spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListener.java | 编程式事务监听器接口,不支持异步 |
| SourceFilteringListener | spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java | 按事件 source 身份过滤的委托监听器 |
| ContextRefreshedEvent 等 | spring-context/src/main/java/org/springframework/context/event/ | 容器生命周期事件 |