registerListeners() 是 Spring 事件机制从"装配"走向"可用"的关键一步。在 initApplicationEventMulticaster() 准备好了广播器之后,registerListeners() 负责把散落在各处的监听器收集起来,注册到这个广播器里,让事件发布真正具备接收方。
它在 refresh() 中的位置如下,紧接在 onRefresh() 之后、finishBeanFactoryInitialization() 之前:
java
// 初始化事件多播器
initApplicationEventMulticaster();
// 留给子类初始化其他特殊 Bean
onRefresh();
// 检查并注册监听器 Bean
registerListeners();
// 实例化所有剩余的单例 Bean
finishBeanFactoryInitialization(beanFactory);
此时,广播器刚刚就绪,而绝大多数普通 Bean 尚未实例化 ,这正是理解 registerListeners() 设计精妙之处的关键背景。
🧩 方法源码与三步逻辑
registerListeners() 的方法体清晰分为三段,每段都有明确的目标:
java
protected void registerListeners() {
// 1. 注册“静态”指定的监听器(非 Bean 方式)
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 2. 从容器中找出所有 ApplicationListener 类型的 Bean,按名称注册
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 3. 发布早期事件(如果存在的话)
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
这三步分别处理了三种不同来源的监听器和事件。
🛡️ 第一步:注册"静态"指定的监听器
java
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
这里的 getApplicationListeners() 获取的是 AbstractApplicationContext 内部维护的一个成员集合 applicationListeners,而不是从 Bean 工厂里查找的。
这些监听器是通过编程方式直接添加 到上下文中的,例如调用 context.addApplicationListener(myListener)。它们不是 Spring 容器管理的 Bean,因此不会经历依赖注入、后置处理器等生命周期。这一步的设计目的,是让开发者能够在容器刷新的早期(比如在 onRefresh() 子类扩展点中)手动注册监听器,而不必等到 Bean 实例化完成。
📋 第二步:按 Bean 名称注册容器中的监听器 Bean
java
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
这是最核心的一步,但有一个极其关键 的细节:方法调用的是 addApplicationListenerBean(listenerBeanName),传入的是Bean 的名称,而不是 Bean 实例本身。
为什么只传名称,不直接实例化? 源码中的注释明确回答了这个问题:
"Do not initialize FactoryBeans here: We need to leave all regular beans uninitialized to let post-processors apply to them!"
如果在这里为了注册监听器而调用 getBean(),就会导致这些监听器 Bean 被提前实例化 。这会破坏 Spring 容器启动的标准流程:正常的 Bean 应该在后续的 finishBeanFactoryInitialization() 步骤中统一实例化,以便所有 BeanPostProcessor(AOP 代理、依赖注入等)都能正常生效。
因此,AbstractApplicationEventMulticaster 内部维护的是一个 "监听器 Bean 名称的集合" 。只有当事件真正发布、需要匹配监听器时,才会根据名称延迟获取 对应的 Bean 实例。getBeanNamesForType 的第三个参数 false 也明确表示不提前初始化 FactoryBean。
同时,getBeanNamesForType 的第二个参数 true 允许包含非单例 Bean,这意味着 prototype 作用域的监听器 Bean 名称也会被收集。
⏳ 第三步:发布早期事件
java
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
在容器启动的早期阶段(registerListeners() 执行之前),如果某个组件调用了 publishEvent(),由于此时广播器尚未就绪 (applicationEventMulticaster 可能为 null),事件无法被广播。
Spring 的解决方案是将这些"过早"发布的事件暂时存入 earlyApplicationEvents 集合中。等到 registerListeners() 执行时,广播器已经就绪,监听器也刚刚注册完毕,此时便可以把积压的早期事件一次性广播出去。
发布完成后,earlyApplicationEvents 被立即置为 null,这是一个明确的状态切换信号:从此之后,事件发布将直接走正常的实时广播流程,不再需要缓存。
🔗 与 @EventListener 的关系:一个容易混淆的边界
到这里,ApplicationListener 接口的实现 Bean 已经全部"登记在册"。但 Spring 还支持用 @EventListener 注解来声明监听方法,这类监听器并不实现 ApplicationListener 接口 ,因此不会被 registerListeners() 收集。
@EventListener 的处理发生在更晚的阶段:finishBeanFactoryInitialization() 完成所有单例 Bean 实例化后,EventListenerMethodProcessor(一个 SmartInitializingSingleton)在 afterSingletonsInstantiated() 回调中扫描所有 Bean 的 @EventListener 方法,并将它们适配为 ApplicationListener 注册到广播器中。
这也解释了 registerListeners() 注释中强调的"leave all regular beans uninitialized"的另一个原因:如果监听器 Bean 在这里被提前实例化,@EventListener 的扫描时机就可能被扰乱,导致注解监听器注册异常。
💎 总结
registerListeners() 的三步逻辑可以概括为:先收编手动注册的监听器,再登记容器中所有监听器 Bean 的"花名册"(只记名字,不叫醒它们),最后清空并发布此前积压的早期事件。
其核心设计原则是延迟实例化 :通过只注册 Bean 名称而非实例,确保了监听器 Bean 仍能在 finishBeanFactoryInitialization() 阶段接受完整的后置处理器处理,与 Spring 容器"先定义、后实例化"的整体节奏保持一致。与 initApplicationEventMulticaster() 的"装配广播器"相比,registerListeners() 完成的是"填充广播器内容"的工作,两者共同构成了事件广播在容器刷新阶段的基础设施。