registerListeners(); 方法详解

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() 完成的是"填充广播器内容"的工作,两者共同构成了事件广播在容器刷新阶段的基础设施。

相关推荐
Jtti1 小时前
香港服务器CPU不高但用户请求排队的原因有哪些?
运维·服务器·网络
福芯微电子有限公司nfc1 小时前
国产收音芯片 FSV4755:多波段调谐与高集成设计,赋能多种音频终端
网络协议·网络安全·音视频·信息与通信·信号处理
道尔柯南1 小时前
Linux 网络编程:从零掌握 TCP Socket 编程(附 C++ 实战)
linux·网络·tcp/ip
星恒讯工业路由器1 小时前
工业路由器在中小 IoT 场景的选型与适配实践:从标准品到定制化的技术路径
开发语言·网络·物联网·php·工业路由器选型·工业网关odm·链路冗余切换
SHENGWEI胜为1 小时前
NVMe SSD写入速度突然下降?从SLC缓存耗尽到温度降速的排查方法
网络·缓存
0+1111 小时前
Linux --ARP的具体过程与ARP欺骗
linux·服务器·网络
shiyi.十一1 小时前
【CISP教材】第8知识域:物理与网络通信安全
网络·信息安全·cisp
科创致远2 小时前
第一代 e+SOP 盒子配显示器实测:物美价廉的产线可视化方案
大数据·网络·人工智能·信息可视化·计算机外设·制造·精益工程
企业数字化笔记2 小时前
AI视频怎么做超分辨率增强?逐帧处理、画幅控制与音画验收
网络·人工智能·音视频