spring-event-事件监听机制实现
1. 事件监听机制概述
1. 场景
模型版本更新了,新版本需要继承老版本的构件分享、自定义属性、着色数据,以后还可能有其他数据要继承,这些数据之间没有直接联系,就是当模型版本变更的时候,他们各自需要执行。
2. 涉及的三个对象
- 事件源(提供事件处理时的元数据)
这里就是模型版本更新了
- 监听器(事件处理者)
构件分享继承是一个监听者,监听到版本更新的事件,需要自己处理点东西
自定义属性继承也是一个监听者。。
着色数据继承也是一个监听者。。。
- 事件发布者(调用者)
2. 示例
1. 事件源 ApplicationEvent
定义一个事件源,只需要继承 org.springframework.context.ApplicationEvent.ApplicationEvent
或其子类
package com.demo.event; import org.springframework.context.ApplicationEvent; /** * 事件源:模型变更了 * 伪代码:模型变更 * */ public class ModelExchangeEvent extends ApplicationEvent { private Integer modelId; private Integer modelVersion; /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ public ModelExchangeEvent(Object source, Integer modelId, Integer modelVersion) { super(source); this.modelId = modelId; this.modelVersion = modelVersion; } public Integer getModelId() { return modelId; } public Integer getModelVersion() { return modelVersion; } public void setModelId(Integer modelId) { this.modelId = modelId; } public void setModelVersion(Integer modelVersion) { this.modelVersion = modelVersion; } }
2. 监听者 ApplicationListener
监听器可以有多个,每个监听器代表一种操作。
- 通过继承
ApplicationListener
实现监听器
package com.demo.event; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 伪代码:着色继承 * 实现 `ApplicationListener` 方式 * */ @Component @Slf4j public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> { @Override public void onApplicationEvent(ModelExchangeEvent e) { log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("着色继承结束!"); } }
- 通过
@EventListener
注解实现监听器
package com.demo.event; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 伪代码:继承 * 使用 @EventListener 方式 * */ @Component @Slf4j public class ModelExchangePropertiesListener { /** * 属性继承 * */ @EventListener(ModelExchangeEvent.class) public void onEvent(ModelExchangeEvent e){ log.info("收到模型{}版本{}的变更通知!自定义属性继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("自定义属性继承结束!"); } /** * 分享继承 * */ @EventListener(ModelExchangeEvent.class) public void sss(ModelExchangeEvent e){ log.info("收到模型{}版本{}的变更通知!分享继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("分享继承结束!"); } }
3. 事件发布者 ApplicationEventPublisher
实现了 ApplicationEventPublisher
接口,就是一个发布者,通过 publishEvent 方法发布事件。
- 直接注入
ApplicationEventPublisher
对象
package com.demo.controller; import com.demo.event.ModelExchangeEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/model") @Slf4j public class ModelController{ /** * 使用 ApplicationEventPublisher 发布事件 * */ @Autowired private ApplicationEventPublisher ap; @RequestMapping("/exchange") public void exchange(){ log.info("伪代码:模型变更的逻辑"); ap.publishEvent(new ModelExchangeEvent(ap, 9001, 2)); } }
这里通过自动注入一个 ApplicationEventPublisher
来发布事件,这个注入的 publisher 其实就是启动的 Spring 容器对象 ConfigurableApplicationContext run = SpringApplication.run(EventMain.class, args)
,也就是这个 run
。
- 通过
ApplicationEventPublisherAware
来手动设置 publisher
package com.demo.controller; import com.demo.event.ModelExchangeEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/model") @Slf4j public class ModelController implements ApplicationEventPublisherAware{ /** * 使用 ApplicationEventPublisher 发布事件 * */ private ApplicationEventPublisher ap; @RequestMapping("/exchange") public void exchange(){ log.info("伪代码:模型变更的逻辑"); this.ap.publishEvent(new ModelExchangeEvent(ap, 9001, 2)); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.ap = applicationEventPublisher; } }
跟上面差距不大
3. 重要组件说明
1. ApplicationEvent
Spring中所有事件的基类,继承自java.util.EventObject。开发人员可以通过继承ApplicationEvent类来创建自定义事件,在事件对象中封装相关信息。事件可以同步或异步触发,并支持条件事件和层次事件等特性。
2. ApplicationListener
是一个监听器接口,用于处理特定类型的事件。当被感兴趣的事件发生时,容器会调用相应的监听器方法,传入该事件作为参数。开发人员可以实现ApplicationListener接口来创建自己的监听器,然后使用@EventListener注解或者配置文件的方式进行注册。
3. ApplicationEventMulticaster
是一个事件广播器,将事件发送给所有已注册的ApplicationListener。默认情况下,Spring使用SimpleApplicationEventMulticaster实现事件广播,但也可以通过配置使用其他实现方式,例如AsyncApplicationEventMulticaster和SimpleThreadScopeEventMulticaster。
就是通过这玩意来调用每个 listener 的处理逻辑。
4. ApplicationEventPublisher
发布者,通过这个组件来发布事件。
4. 多个监听者执行顺序
默认情况下多个监听者同步执行,使用 @Order 注解标注执行顺序。
5. 异步调用
也有两种方式。
1. ApplicationEventMulticaster
package com.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import org.springframework.stereotype.Component; import java.util.concurrent.Executor; /** * 配置监听器异步执行 * */ @Configuration public class ListenerConfig { @Bean public ApplicationEventMulticaster applicationEventMulticaster() { //@1 //创建一个事件广播器 SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster(); //给广播器提供一个线程池,通过这个线程池来调用事件监听器 Executor executor = this.applicationEventMulticasterThreadPool().getObject(); //设置异步执行器 result.setTaskExecutor(executor);//@1 return result; } //提供一个线程池 @Bean public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() { ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean(); result.setThreadNamePrefix("modelThread-"); result.setCorePoolSize(2); return result; } }
- 原理
自定义 ApplicationEventMulticaster,并设置其线程池,监听器最终是通过ApplicationEventMulticaster内部的实现来调用的,所以我们关注的重点就是这个类,这个类默认有个实现类 SimpleApplicationEventMulticaster
,这个类是支持监听器异步调用的,内部有个线程池字段:
private Executor taskExecutor;
SimpleApplicationEventMulticaster 执行逻辑:
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { //线程池对象不为 null,就提交自定义监听事件到线程池中 executor.execute(() -> invokeListener(listener, event)); } else { //线程池对象为 null,就顺序执行每个 listener 自定义监听事件 invokeListener(listener, event); } } }
所以我们需要定义一个 SimpleApplicationEventMulticaster
的 bean 并设置其线程池.
- 弊端:这样会使所有的监听器都异步执行
2. @Async
@EnableAsync
标注在启动类上,来标识支持异步。@Async
标注在指定 listener 上。这种应该就是传说中 bean 的异步执行。
package com.demo.event; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 伪代码:着色继承 * 实现 ApplicationListener 方式 * */ @Component @Slf4j @Async public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> { @Override public void onApplicationEvent(ModelExchangeEvent e) { log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("着色继承结束!"); } }
package com.demo.event; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 伪代码:继承 * 使用 @EventListener 方式 * */ @Component @Slf4j public class ModelExchangePropertiesListener { /** * 属性继承 * */ @EventListener(ModelExchangeEvent.class) @Async public void onEvent(ModelExchangeEvent e){ log.info("收到模型{}版本{}的变更通知!自定义属性继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("自定义属性继承结束!"); } /** * 分享继承 * */ @EventListener(ModelExchangeEvent.class) @Async public void sss(ModelExchangeEvent e){ log.info("收到模型{}版本{}的变更通知!分享继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("分享继承结束!"); } }
package com.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class EventMain { public static void main(String[] args) { SpringApplication.run(EventMain.class, args); } }
- 默认的线程池
SimpleAsyncTaskExecutor
不太行,可以通过@Async("applicationEventMulticasterThreadPool")
自己换一个。
package com.demo.event; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 伪代码:着色继承 * 实现 ApplicationListener 方式 * */ @Component @Slf4j @Async("applicationEventMulticasterThreadPool") public class ModelExchangeColorListener implements ApplicationListener<ModelExchangeEvent> { @Override public void onApplicationEvent(ModelExchangeEvent e) { log.info("收到模型{}版本{}的变更通知!着色继承开始!", e.getModelId(), e.getModelVersion()); //模拟逻辑运行 try { Thread.sleep(2_000); } catch (InterruptedException ex) { ex.printStackTrace(); } log.info("着色继承结束!"); } }
自定义的线程池
package com.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import java.util.concurrent.Executor; /** * 配置监听器异步执行 * */ @Configuration public class ListenerConfig { //提供一个线程池 @Bean public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() { ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean(); result.setThreadNamePrefix("modelThread-"); result.setCorePoolSize(2); return result; } }
参考文献
https://blog.csdn.net/lin1214000999/article/details/124707979
原理参考:https://cloud.tencent.com/developer/article/2364790
本文作者:primaryC
本文链接:https://www.cnblogs.com/cnff/p/18061283
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步