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