分析Spring-ApplicationListener监听器

Spring事件监听机制
本文详细介绍了Spring框架中事件监听机制的实现原理,包括ApplicationListener接口的作用、事件多播器的初始化过程以及事件的注册和发布流程。

目录

一、概述

二、运行流程


一、概述

ApplicationListener接口用来监听容器发布的事件,用来事件驱动模型开发。

在方法上使用@EventListener注解,作用与实现ApplicationListener接口一致,都可用来监听事件。

二、运行流程

2.1 容器在refresh方法的prepareBeanFactory方法中添加了类型为ApplicationListenerDetector的后置处理器。

2.1.1 ApplicationListenerDetector后置处理器主要是用来搜索发现实现了ApplicationListener接口bean(包括innerBean也会被搜索到),将其添加到容器的监听器集合中。

因为innerBean不能通过容器的getBeanNamesForType方法得到,所以在refresh方法registerListeners方法中无法将innerBean监听器添加到多播器中。

innerBean 参考什么是Spring inner beans?_Leon_Jinhai_Sun的博客-优快云博客

2.1.2 在组件创建初始化完成后,调用postProcessAfterInitialization方法,将实现了ApplicationListener接口的组件对象(包括innerBean对象)通过容器的addApplicationListener方法添加到监听器集合中。

2.2 在refresh方法中调用initApplicationEventMulticaster方法,初始化applicationEventMulticaster应用事件多播器(派发器)。事件多播器主要是在容器发布事件时,通过遍历applicationEventMulticaster事件多播器的监听器集合,将事件发布到各个监听器中。

2.2.1 initApplicationEventMulticaster方法中先判断容器是否有组件id为applicationEventMulticaster的事件多播器对象。若是有,则从容器中获取组件对象,并赋值给容器的applicationEventMulticaster属性。

若是没有,则容器会默认创建类型为SimpleApplicationEventMulticaster的对象,并赋值给容器的applicationEventMulticaster属性和添加到容器的单实例集合中。

我们可以自定义创建事件多播器对象,并加入到容器的单实例集合中。

2.3 通过refresh方法的registerListeners方法注册监听器 。

2.3.1 registerListeners方法中,先遍历容器之前存储的applicationListeners监听器集合,调用事件多播器的addApplicationListener方法,将监听器对象添加到事件多播器的defaultRetriever.applicationListeners中。

2.3.2 遍历容器中实现了ApplicationListener接口的listenerBeanNames集合,并添加到事件多播器的defaultRetriever.applicationListenerBeans中。

2.3.3 接着将早期存储的earlyApplicationEvents事件集合通过事件多播器的multicastEvent方法发布事件,将容器的earlyApplicationEvents设置为null。

2.3.4 查看ApplicationEventMulticastermulticastEvent方法,先获取到event事件的类型type,接着调用getApplicationListeners方法获取可以监听该事件event的监听器集合。

2.3.5 在getApplicationListeners方法中,先判断事件多播器的retrieverCache缓存中是否可以找到该事件的监听器集合。若是找到,则直接返回缓存中的监听器集合。

若是找不到,则接着通过retrieveApplicationListeners方法找到可以监听该事件的监听器集合并返回。

2.3.6 在retrieveApplicationListeners方法中,先遍历listenersdefaultRetriever.applicationListeners)​​​​​​,​ 通过supportsEvent方法判断该监听器是否可以监听该事件,若可以,则加入到allListeners集合中。

2.3.6.1  在supportsEvent方法中,先判断监听器是否实现了GenericApplicationListener接口。

若是实现了,则将监听器转换成GenericApplicationListener赋值给smartListener,如ApplicationListenerMethodAdapter(标识@EventListener的方法生成的监听器)实现该接口。

而实现ApplicationListener接口的监听器没实现该接口,所以通过创建GenericApplicationListenerAdapter赋值给smartListener,然后调用GenericApplicationListenersupportsEventType方法。

2.3.6.2 GenericApplicationListener接口实现类如下图。

GenericApplicationListenerAdapter的构造器中,通过resolveDeclaredEventType方法解析监听器,得到监听器可以监听事件的类型。

GenericApplicationListenerAdaptersupportsEventType中,通过isAssignableFrom方法判断eventType监听事件的类型是否是监听器监听事件的类型本身或实现类(继承或实现了监听器监听事件的类型)。若是,则表示该监听器可以监听此事件。

2.3.7 接着listenerBeans集合若是不为空,接着遍历listenerBeans集合,判断allListeners集合是否包含遍历的监听器对象。若是不包含,则通过supportsEvent方法判断监听器是否可以监听该事件,若可以,则将其添加到allListeners集合, 最后返回allListeners集合。

2.3.8 遍历返回的监听器集合,判断事件多播器是否有Executor类型的异步对象。若是有,则通过异步对象异步运行invokeListener方法去运行监听器的onApplicationEvent方法,若是没有,则同步运行监听器的invokeListener方法。

2.4 容器通过preInstantiateSingletons方法,创建并初始化完所有非延迟加载的单实例组件对象后,接着遍历容器的beanDefinitions集合,找到实现SmartInitializingSingleton接口的单实例对象,调用对象的afterSingletonsInstantiated方法。

2.4.1 EventListenerMethodProcessor处理器实现SmartInitializingSingleton接口,该处理器主要是解析@EventListener注解,通过afterSingletonsInstantiated方法的processBean方法将标识@EventListener注解的方法封装成ApplicationListenerMethodAdapter的监听器对象并加入到多播器中。

参考分析事件监听器注解:@EventListener、@TransactionalEventListener_Just-Today的博客-优快云博客

2.5 通过容器发布事件,调用容器的publishEvent方法,参数可以是ApplicationEventObject或它们的子类。 

统一调用了publishEvent(Object event, @Nullable ResolvableType eventType)方法,在该方法中,先判断event是否实现了ApplicationEvent接口,若是有,则转换成ApplicationEvent。若是没有,event则通过创建PayloadApplicationEvent对象来构建ApplicationEvent

接着调用多播器的multicastEvent方法发布事件,流程就是2.3.4节

参考分析事件监听器注解:@EventListener、@TransactionalEventListener_Just-Today的博客-优快云博客

<think>我们正在讨论Spring Cloud Bus与AMQP(如RabbitMQ)集成时的事件监听机制。根据提供的引用内容,我们可以总结如下: 1. Spring Cloud Bus通过消息中间件(RabbitMQ或Kafka)连接分布式系统的节点,利用消息中间件的广播机制传播消息。 2. 当配置中心(如Spring Cloud Config Server)触发配置更新事件时,Spring Cloud Bus会将该事件作为消息发布到消息中间件。 3. 各个微服务节点通过监听特定的队列来接收这些消息,然后进行相应的处理(如配置刷新)。 具体到事件监听的使用方法和实现原理: ### 使用方法 1. **添加依赖**:在微服务项目中,需要添加`spring-cloud-starter-bus-amqp`依赖(同时需要`spring-boot-starter-actuator`以提供刷新端点)。如引用[4]所示: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> ``` 2. **配置RabbitMQ连接信息**:在`application.yml`或`application.properties`中配置RabbitMQ的连接信息,例如: ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest ``` 3. **启用配置刷新**:在需要刷新配置的Bean上使用`@RefreshScope`注解。 4. **触发配置更新事件**:当配置中心更新配置后,向任意一个微服务实例发送一个POST请求到`/actuator/bus-refresh`端点(需要先暴露该端点),该实例会通过Spring Cloud Bus将事件发布到消息中间件,然后广播到所有微服务节点。 ### 实现原理 1. **消息监听器的创建**:当应用启动时,Spring Cloud Bus会自动配置与RabbitMQ的连接,并创建消息监听器容器。这些监听器会订阅特定的交换机(Exchange)和队列(Queue)。引用[1]提到,Spring Cloud Bus会创建一个或多个特定的消息监听器。 2. **消息发布与订阅**: - 事件发布:当配置更新事件被触发(例如通过调用`/actuator/bus-refresh`端点),Spring Cloud Bus会将该事件封装成一个消息,发布到RabbitMQ的一个特定的交换机(默认为`springCloudBus`)上。 - 事件订阅:所有连接到同一消息中间件的微服务节点都会通过它们自己的队列绑定到这个交换机,从而接收到消息。 3. **消息处理**: - 每个微服务节点上的消息监听器一旦接收到消息,就会解析消息内容(事件类型、数据等)。 - 对于配置刷新事件,监听器会触发本地环境的刷新(例如刷新`@RefreshScope`注解的Bean),从而实现配置的动态更新。 4. **广播机制**:Spring Cloud Bus利用消息中间件的广播机制(如RabbitMQ的Fanout Exchange或者Topic Exchange)将消息分发到所有订阅的节点。引用[2]指出其实质是利用了MQ的广播机制在分布式系统中传播消息。 5. **事件传播**:事件通过消息中间件传播,因此即使微服务节点不在同一台物理机器上,只要连接到同一个消息中间件,就能接收到事件并处理。 ### 总结 Spring Cloud Bus AMQP的事件监听机制通过消息中间件(如RabbitMQ)实现了分布式系统中事件的发布与订阅。使用上只需添加依赖、配置MQ连接、并利用`@RefreshScope`和`/actuator/bus-refresh`端点即可。实现原理上,它依赖于消息中间件的广播机制,每个微服务节点通过监听消息队列来接收事件,然后执行相应的处理逻辑。 §§相关问题§§ 1. 如何在Spring Cloud Bus中自定义事件和监听器? 2. 如何确保Spring Cloud Bus的消息可靠传递(不丢失)? 3. 如何监控Spring Cloud Bus的消息流量? </think>### Spring Cloud Bus AMQP 事件监听:使用方法和实现原理 #### **一、使用方法** 1. **依赖配置** 在微服务的 `pom.xml` 中添加关键依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> ``` 同时需配置 RabbitMQ 连接信息(`application.yml`): ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest ``` 2. **暴露刷新端点** 在配置中心或客户端服务的 `application.yml` 中启用总线刷新端点: ```yaml management: endpoints: web: exposure: include: "bus-refresh" ``` 3. **触发配置更新** 向任意微服务发送 POST 请求刷新配置: ```bash curl -X POST http://<service-host>:<port>/actuator/bus-refresh ``` 此时配置变更事件将通过 RabbitMQ 广播到所有订阅的微服务[^3]。 4. **监听自定义事件** 开发者可定义自定义事件和监听器: ```java // 1. 定义事件 public class CustomEvent extends RemoteApplicationEvent { public CustomEvent(Object source, String originService) { super(source, originService); } } // 2. 发布事件(在服务中) @Autowired private ApplicationEventPublisher eventPublisher; eventPublisher.publishEvent(new CustomEvent(data, "service-A")); // 3. 监听事件(任意服务中) @EventListener public void handleCustomEvent(CustomEvent event) { System.out.println("收到事件: " + event.getSource()); } ``` --- #### **二、实现原理** 1. **消息驱动架构** Spring Cloud Bus 基于消息中间件(RabbitMQ/Kafka)构建分布式通信通道。当事件触发时: - **生产者**:将事件封装为消息发送到 RabbitMQ 的 `springCloudBus` Exchange[^1]。 - **消费者**:各微服务通过监听队列(如 `springCloudBus.anonymous.<ID>`)接收消息[^2]。 2. **动态监听器注册** 服务启动时自动注册消息监听器容器(`SimpleMessageListenerContainer`),其核心行为: - 绑定到 RabbitMQ 的 Topic Exchange(`springCloudBus`)。 - 创建临时队列(AnonymousQueue)监听广播消息[^1]。 3. **事件传播流程** ```mermaid graph LR A[配置中心触发/bus-refresh] --> B[Spring Cloud Bus] B --> C[发布消息到RabbitMQ Exchange] C --> D[Exchange广播到所有服务队列] D --> E[服务监听器解析消息] E --> F[触发本地配置刷新或自定义事件处理] ``` 4. **消息协议与序列化** - 使用 `RemoteApplicationEvent` 封装事件元数据(源服务、事件类型)。 - 消息体通过 Jackson 序列化为 JSON,确保跨语言兼容性[^3]。 5. **广播与单播机制** - **广播**:默认将消息发送给所有服务(Fanout 模式)。 - **定向推送**:可通过 `destination` 参数指定目标服务(如 `bus-refresh?destination=service-B:**`)[^5]。 --- #### **三、关键特性** 1. **自动配置** `@EnableBus` 注解自动初始化连接工厂、消息模板和监听容器[^4]。 2. **幂等性处理** 通过事件 ID 去重,避免消息重复消费导致多次刷新。 3. **健康监控** 集成 `/actuator/health` 端点,可监控 RabbitMQ 连接状态。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值