惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
Y
Y Combinator Blog
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
M
MIT News - Artificial intelligence
博客园 - Franky
V
Visual Studio Blog
I
InfoQ
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
博客园 - 司徒正美
L
LangChain Blog

博客园 - wastonl

maven选择settings.xml策略 RocketMQ消费机制整理 RocketMQ重平衡机制 @JacksonXmlElementWrapper使用限制 JSR303和SpringBoot参数校验机制 同一Java进程生产者和消费者与MqClientInstance对应关系 RocketMQ自动创建主题机制 SpringMVC异步请求 tomcat异步请求机制 Spring异步任务和定时任务 Spring ResolvableType说明 Jvm内存以及垃圾回收相关知识 RocketMQ如何保证消息可靠性 RocketMQ整体架构 mybatis-plus易忘点笔记 SpringMVC使用Resource实现二进制传输(下载) Spring Bean销毁机制 Spring Lifecycle组件 Zipkin Brave使用 Spring Boot日志系统简要介绍 spring cloud sleuth基本使用 Spring懒加载与@Lazy注解 tomcat自动刷新响应输出流缓冲区 https碎碎念 ES脚本使用 SpringMVC静态资源处理 Maven插件运行方式 如何使用Maven将项目中的依赖打进jar包 时区以及时区对于Java时间类格式化的影响 SpringMVC处理请求头、响应头、编码行为
Spring事件异步执行设计与实现
wastonl · 2026-03-13 · via 博客园 - wastonl

背景

默认情况下,Spring的事件监听是同步执行的,也就是publishEvent方法来触发ApplicationListener执行的。当然了也可以配置成异步去执行,但是这是全局的,所有的事件都会变成异步执行了(需要自己配置一个ApplicationEventMulticaster并给它设置一个线程池)。

设计与实现

AsyncEvent.java

/**
 * 所有异步事件的基类
 */
public abstract class AsyncEvent<T> extends ApplicationEvent {
    @Serial
    private static final long serialVersionUID = -3876435908556202255L;

    private final T payload;

    public AsyncEvent(T payload, Object source) {
        super(source);
        this.payload = payload;
    }

    public T getPayload() {
        return payload;
    }
}

AsyncEventListener.java

/**
 * 监听器, 将事件分发到线程池中执行
 */
package com.wangtao.springboot3.event;

import com.wangtao.springboot3.event.handler.AsyncEventHandler;
import com.wangtao.springboot3.executor.CatchExceptionThreadFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author wangtao
 * Created at 2026-03-13
 */
@Component
public class AsyncEventListener implements DisposableBean {

    private final ThreadPoolTaskExecutor taskExecutor;

    private final List<AsyncEventHandler<?>> asyncEventHandlerList;

    private boolean useDefaultTaskExecutor;

    public AsyncEventListener(@Qualifier("asyncEventTaskExecutor") ObjectProvider<ThreadPoolTaskExecutor> taskExecutorProvider,
                              List<AsyncEventHandler<?>> asyncEventHandlerList) {
        this.taskExecutor = taskExecutorProvider.getIfAvailable(() -> {
            this.useDefaultTaskExecutor = true;
            return this.defaultTaskExecutor();
        });
        this.asyncEventHandlerList = asyncEventHandlerList;
        // 根据@Order或者Ordered接口排序
        AnnotationAwareOrderComparator.sort(this.asyncEventHandlerList);
    }

    @EventListener
    public void onAsyncEvent(AsyncEvent<?> asyncEvent) {
        try {
            this.taskExecutor.execute(() -> dispatchAsyncEvent(asyncEvent));
        } catch (TaskRejectedException e) {
            // 任务提交失败, 做补偿
        }
    }

    @SuppressWarnings("unchecked")
    private void dispatchAsyncEvent(AsyncEvent<?> asyncEvent) {
        /*
         * 会根据实际的asyncEvent实例来判断交给哪一个handler执行
         * 第一个参数: 必须是拥有泛型参数的class,可以是类,也可是接口
         * 第二个参数: 具体的实例对象列表,它们所属的类必须继承或者实现第一个参数class
         * 底层原理: Class.getGenericSuperclass或者getGenericInterfaces
         * 通过这两个方法可以拿到具体的泛型参数类型了,这样子就可以根据传入的方法参数asyncEvent来进行判断了
         */
        LambdaSafe.callbacks(AsyncEventHandler.class, asyncEventHandlerList, asyncEvent)
            .invoke(asyncEventHandler -> asyncEventHandler.handle(asyncEvent));
    }

    @Override
    public void destroy()  {
        if (this.useDefaultTaskExecutor) {
            this.taskExecutor.destroy();
        }
    }

    private ThreadPoolTaskExecutor defaultTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(4);
        taskExecutor.setMaxPoolSize(8);
        taskExecutor.setThreadFactory(new CatchExceptionThreadFactory("async-event"));
        taskExecutor.setQueueCapacity(1000);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(20);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

AsyncEventHandler.java

/**
 * 真正执行事件的接口
 */
public interface AsyncEventHandler<T extends AsyncEvent<?>> {

    void handle(T asyncEvent);
}

使用与测试

public class LikeAsyncEvent extends AsyncEvent<Integer> {

    @Serial
    private static final long serialVersionUID = 1761508846850771470L;

    public LikeAsyncEvent(Integer payload, Object source) {
        super(payload, source);
    }
}
public class CommentAsyncEvent extends AsyncEvent<String> {

    @Serial
    private static final long serialVersionUID = 1761508846850771470L;

    public CommentAsyncEvent(String payload, Object source) {
        super(payload, source);
    }
}
@Slf4j
@Component
public class LikeAsyncEventHandler implements AsyncEventHandler<LikeAsyncEvent> {

    @Override
    public void handle(LikeAsyncEvent asyncEvent) {
        log.info("======handle likeAsyncEvent, data: {}", asyncEvent.getPayload());
    }
}
@Slf4j
@Component
public class CommentAsyncEventHandler implements AsyncEventHandler<CommentAsyncEvent> {

    @Override
    public void handle(CommentAsyncEvent asyncEvent) {
        log.info("======handle commentAsyncEvent, data: {}", asyncEvent.getPayload());
    }
}
@Test
public void asyncEventTest() {
    applicationContext.publishEvent(new LikeAsyncEvent(1, this));
}