Spring的事件机制
Spring的事件包含三部分
- 事件(ApplicationEvent):继承自 jdk 的 EventObject,所有的事件都需要继承 ApplicationEvent,并且通过构造器参数 source 得到事件源.
- 应用事件监听器(ApplicationListener):继承自 jdk 的 EventListener,所有的监听器都要实现这个接口
- 事件发布者(ApplicationEventPublisher,ApplicationContext 实现了 ApplicationEventPublisher 接口):具体实现类是 SimpleApplicationEventMulticaster,在 Spring 中维护监听者。
ApplicationEventMulticaster#multicastEvent
方法将事件传给监听器
Spring默认事件类型
- ContextRefreshedEvent:在初始化或刷新时 ApplicationContext 发布
- ContextStartedEvent:通过在 ConfigurableApplicationContext 接口上使用 start() 方法启动 ApplicationContext 时发布,通常,此信号用于在显式停止后重新启动 bean
- ContextStoppedEvent:使用 ConfigurableApplicationContext 接口上的 stop()方法停止 ApplicationContext 时发布,停止的上下文可以通过 start() 调用重新启动
- ContextClosedEvent:通过使用 ConfigurableApplicationContext 接口上的 close() 方法或通过 JVM shutdown hook 钩子函数在 ApplicationContext 被关闭时发布。在这里,“关闭”意味着所有单例 bean 都将被销毁
- RequestHandledEvent:一个特定于 web 的事件,它告诉所有 bean 一个 HTTP 请求已经得到了服务。此事件在请求完成后发布。此事件仅适用于使用 Spring 的 DispatcherServlet 的 Web 应用程序
自定义事件
注解方式
新建一个用户注册事件,继承ApplicationEvent
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(User user) {
super(user);
}
}
创建两个事件监听器,@EventListener
注解用在方法上
@Slf4j
@Service
public class UserRegisterListener {
@EventListener
public void sendMsg(UserRegisterEvent userRegisterEvent) {
User user = (User) userRegisterEvent.getSource();
log.info("发送短信,手机号{}", user.getPhone());
}
@EventListener
public void sendEmail(UserRegisterEvent userRegisterEvent) {
User user = (User) userRegisterEvent.getSource();
log.info("发送邮箱,邮箱{}", user.getEmail());
}
}
在用户注册逻辑完成后,发布用户注册事件
@Service
public class UserServiceImpl {
public final ApplicationEventPublisher applicationEventPublisher;
public UserServiceImpl(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void register() {
log.info("用户注册");
// 发送用户注册事件
applicationEventPublisher.publishEvent(new UserRegisterEvent(User.builder().name("lisi").email("abc@xxx.com").phone("8008208820").build()));
}
}
运行结果:
可以看到事件已经发布,并且监听成功。
但是从上图可以看到,主逻辑和监听器的线程是同一个,也就是在同一个线程中执行,如果想多线程做异步处理,该怎么做?
事件的发布是在事件广播器的ApplicationEventMulticaster#multicastEvent
方法,具体的实现是SimpleApplicationEventMulticaster#multicastEvent
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) {
// 线程池不为空,监听器交给线程池处理
executor.execute(() -> invokeListener(listener, event));
}
else {
// 线程池为空,执行监听器
invokeListener(listener, event);
}
}
}
在 Spring 启动时,会初始化事件广播器AbstractApplicationContext#initApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断容器是否包含了 ApplicationEventMulticaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 没有就创建一个,并注册到容器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
如果容器不包含ApplicationEventMulticaster
,就会创建一个SimpleApplicationEventMulticaster
,这时候executor
就会为空。
如果我们自己写一个SimpleApplicationEventMulticaster
的Bean实例,并且给实例设置线程池的属性,那监听器就会交给线程池处理,实现异步处理
@Configuration
public class MyConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster () {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(createTaskExecutor());
return simpleApplicationEventMulticaster;
}
private ExecutorService createTaskExecutor() {
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("applicationEventMulticaster-");
return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), threadFactory);
}
}
运行结果:
由上图可以发现,监听器已经由线程池处理,做到了主逻辑和监听器的异步
---
欢迎小伙伴一起探讨,一起学习,一起成长
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix