游乐游手机版
首页/编程语言/文章详情

Spring监听器之ApplicationListener原理及源码深度解析

时间:2026-04-25 21:41
一、原理及源码解析 在Spring框架中,事件驱动模型是一个核心但常被忽视的机制。它主要围绕几个关键事件展开:ContextRefreshedEvent、开发者自定义发布的事件(如示例中的IOCTest_Ext$1[source=我发布的事件]),以及ContextClosedEvent。 那么,这

一、原理及源码解析

在Spring框架中,事件驱动模型是一个核心但常被忽视的机制。它主要围绕几个关键事件展开:ContextRefreshedEvent、开发者自定义发布的事件(如示例中的IOCTest_Ext$1[source=我发布的事件]),以及ContextClosedEvent

那么,这些事件是如何产生并被处理的呢?我们不妨深入到源码层面,一探究竟。

事件的生命周期与触发时机

  • ContextRefreshedEvent事件:它的诞生与容器的生命周期紧密相连。
    1. 容器在创建对象时,会调用核心的refresh()方法。
    2. refresh()流程执行完毕,即容器刷新完成时,会通过finishRefresh()方法发布ContextRefreshedEvent事件。
  • 自定义发布事件:开发者可以在任何业务逻辑中,通过applicationContext.publishEvent()主动发布事件。
  • ContextClosedEvent事件:当容器关闭时,会自动发布此事件。

【事件发布流程】源码执行流程

当调用publishEvent(new ContextRefreshedEvent(this));时,幕后发生了什么?

  1. 获取事件派发器:首先,Spring会通过getApplicationEventMulticaster()获取事件的多播器(或称派发器)。
  2. 派发事件:接着,调用multicastEvent来派发事件。
  3. 获取监听器:派发器会获取所有与当前事件匹配的ApplicationListener。这个过程大致如下:
    for (final ApplicationListener listener : getApplicationListeners(event, type)) {
    1. 异步派发支持:如果配置了Executor,Spring会使用它进行异步派发,提升性能。
    2. 同步执行:如果没有配置Executor,则采用同步方式,直接调用invokeListener(listener, event);。最终,会回调监听器的onApplicationEvent方法。

【事件多播器(派发器)】源码执行流程

这个关键的派发器又是如何初始化的呢?

  1. 同样始于容器的refresh()方法。
  2. initApplicationEventMulticaster()方法中,Spring会初始化ApplicationEventMulticaster
    1. 它会先去容器中查找是否存在id="applicationEventMulticaster"的组件。
    2. 如果找不到,Spring会创建一个默认的SimpleApplicationEventMulticaster(beanFactory)实例,并将其注册到容器中。这样一来,其他需要派发事件的组件就能自动注入这个多播器了。

【容器中有哪些监听器】源码执行流程

监听器又是如何被收集并注册到派发器中的?

  1. 依旧是refresh()方法。
  2. registerListeners()阶段,Spring会从容中找出所有类型为ApplicationListener的Bean。
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    //将listener注册到ApplicationEventMulticaster中
    getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

SmartInitializingSingleton 原理

除了事件监听,还有一个与之相关的接口SmartInitializingSingleton,其核心方法是afterSingletonsInstantiated()。它的执行时机是:

  1. IoC容器创建对象并执行refresh()
  2. finishBeanFactoryInitialization(beanFactory)阶段,初始化所有剩余的单例Bean。
    1. 首先,创建所有的单实例Bean(调用getBean())。
    2. 然后,遍历所有创建好的单例Bean,判断其是否实现了SmartInitializingSingleton接口。如果是,则调用其afterSingletonsInstantiated()方法。

二、实例

理论说得再多,不如动手一试。ApplicationListener是监听容器中发布事件的核心接口,它正是Spring事件驱动模型开发的基石。

该接口定义为:public interface ApplicationListener,意味着它可以监听ApplicationEvent及其所有子类事件。

使用起来,通常遵循以下几个步骤:

  1. 编写监听器:创建一个类来实现ApplicationListener接口,或者更简单地,在任意Bean的方法上使用@EventListener注解。后者的原理在于,Spring容器中有一个EventListenerMethodProcessor处理器,专门负责解析方法上的这个注解。
  2. 纳入容器管理:通过@Component@Service等注解,将监听器类装配到Spring容器中。
  3. 监听事件:只要容器中发布了相关事件,注册的监听器就能自动监听到。例如:
    • ContextRefreshedEvent:当容器刷新完成,所有Bean都完全创建后发布。
    • ContextClosedEvent:当容器关闭时发布。
  4. 发布事件:在任何可以获取到ApplicationContext的地方,调用applicationContext.publishEvent()方法,即可发布自定义事件。

1、自定义监听器

首先,我们可以定义一个实现ApplicationListener接口的类。这样,它就能监听所有类型的ApplicationEvent。别忘了用@Component将其装配到Spring容器中。

package com.atguigu.ext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationListener implements ApplicationListener {
	//当容器中发布此事件以后,方法触发
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		// TODO Auto-generated method stub
		System.out.println("收到事件:"+event);
	}
}

另一种更灵活的方式是使用@EventListener注解。你可以将它标注在任何一个Bean的方法上。注意,@EventListener(classes={ApplicationEvent.class})中的classes属性可以指定多个要监听的类,用逗号分隔即可。

package com.atguigu.ext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
public class UserService {
	@EventListener(classes={ApplicationEvent.class})
	public void listen(ApplicationEvent event){
		System.out.println("UserService。。监听到的事件:"+event);
	}
}

2、定义配置类,加载IOC容器

接下来,需要一个配置类来扫描并加载这些组件。

@ComponentScan("com.atguigu.ext")
@Configuration
public class ExtConfig {
	@Bean
	public Blue blue(){
		return new Blue();
	}
}

3、测试

最后,我们编写测试代码来验证。在测试中,我们不仅会触发Spring内置的容器刷新和关闭事件,还会主动发布一个自定义事件(这里使用了匿名内部类来创建ApplicationEvent)。

package com.atguigu.test;
import org.junit.Test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.atguigu.ext.ExtConfig;
public class IOCTest_Ext {
	@Test
	public void test01(){
		AnnotationConfigApplicationContext applicationContext  = new AnnotationConfigApplicationContext(ExtConfig.class);
		//发布事件;
		applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
		});
		applicationContext.close();
	}
}

运行结果:

Spring监听器之ApplicationListener原理及源码深度解析

Spring监听器之ApplicationListener原理及源码深度解析

结果说明:从控制台输出可以清晰看到,Spring容器自身在刷新和关闭时会发布事件,这些事件被我们自定义的监听器成功捕获。同时,我们主动发布的那个自定义事件“我发布的事件”,也同样被监听到了。这充分证明了,无论是Spring内置的事件,还是开发者自定义的事件,只要监听器注册得当,整个事件驱动模型就能顺畅运转。

来源:https://www.jb51.net/program/362537s4d.htm
上一篇IDEA缓存配置等位置更改过程 下一篇Python元类Metaclass初探:理解类的类
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Kafka与CentOS其他服务协同配置指南
编程语言 · 2026-07-12

Kafka与CentOS其他服务协同配置指南

Kafka在CentOS生态中作为数据流通中枢,与EFK日志收集、HDFS存储、HBase、Prometheus+Grafana监控及SparkStreaming流处理系统协同,通过生产者-消费者模式构建实时数据管道,实现解耦、削峰填谷与高效集成。

如何使用deluser命令重命名用户的详细操作指南
编程语言 · 2026-07-12

如何使用deluser命令重命名用户的详细操作指南

在Linux系统管理中,重命名用户需通过删除旧用户并创建新用户实现。操作包括备份数据、用rsync迁移文件、更改文件所有权、删除旧用户及家目录,最后重新登录验证。不同发行版命令略有差异,建议在测试环境演练。

详细CentOS系统中C++配置常见问题及解决方法大全
编程语言 · 2026-07-12

详细CentOS系统中C++配置常见问题及解决方法大全

CentOS配置C++常见问题包括编译器缺失或版本过旧、环境变量错误、依赖库开发包未装、多版本冲突、权限路径问题、内存不足及内核参数不当。需正确安装gcc-c++及devel包,配置PATH与库路径,使用devtoolset或alternatives管理版本,调整权限与ulimit、sysctl参数。

CentOS C++环境变量配置方法
编程语言 · 2026-07-12

CentOS C++环境变量配置方法

在CentOS系统配置C++编译器需设置路径和动态库路径。先验证g++是否已安装,否则使用sudoyuminstallgcc-c++安装。通过whichg++找到安装路径后,在~ bashrc中添加exportPATH=$PATH:该路径并执行source使之生效。动态库路径可用find命令查找后类似加入LD_LIBRARY_PATH。最后用g++编译测试

CentOS中C++配置文件位置与路径完整说明
编程语言 · 2026-07-12

CentOS中C++配置文件位置与路径完整说明

CentOS中C++配置文件包括系统级全局配置( etc profile、 etc bashrc)影响所有用户,用户级配置(~ bashrc)仅影响当前用户,以及第三方库路径和构建工具CMakeLists txt。这些文件共同设置环境变量、库路径及编译选项等详细参数,用于管理相关开发环境。