Spring MVC AsyncConfigurer中getAsyncExecutor与threadPoolTaskScheduler的区别及场景
问题:Spring中
getAsyncExecutor与threadPoolTaskScheduler的区别及getAsyncExecutor的使用场景 我在Spring的async-instrumentation文档里看到这两个组件,想知道它们的区别。我已经按照文档完成了如下配置:
@Configuration public class ServiceConfig { @Configuration(proxyBeanMethods = false) @EnableAsync static class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return ContextExecutorService.wrap( Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()), ContextSnapshot::captureAll); } } @Bean(name = "taskExecutor", destroyMethod = "shutdown") ThreadPoolTaskScheduler threadPoolTaskScheduler() { final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler() { @Override protected ExecutorService initializeExecutor( final ThreadFactory threadFactory, final RejectedExecutionHandler rejectedExecutionHandler) { final ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler); return ContextExecutorService.wrap(executorService, ContextSnapshot::captureAll); } @Override public ScheduledExecutorService getScheduledExecutor() throws IllegalStateException { return ContextScheduledExecutorService.wrap(super.getScheduledExecutor()); } }; threadPoolTaskScheduler.setThreadNamePrefix("SpTaskExecutor1"); threadPoolTaskScheduler.initialize(); return threadPoolTaskScheduler; } @Bean public WebMvcConfigurer webMvcConfigurerConfigurer(final AsyncTaskExecutor taskExecutor) { return new WebMvcConfigurer() { @Override public void configureAsyncSupport(final AsyncSupportConfigurer configurer) { configurer.setTaskExecutor(taskExecutor); // Added timeout of 60 seconds for async api due to downloads api having 100K rows. configurer.setDefaultTimeout(60 * 1000L /* 60 sec*/); } }; } }
我写了一个简单的Hello World控制器:
public class HelloWorldController { @GetMapping("/hello") public ResponseEntity<StreamingResponseBody> sendGreetings() { logMsg("Hello Outside Body Thread"); return ResponseEntity.ok() .body( outputStream -> { logMsg("Hello Inside Body Thread"); outputStream.write("Hello World".getBytes()); } ); } @GetMapping("/hello1") public ResponseEntity<String> sendGreetings1() { logMsg("Hello1"); return ResponseEntity.ok().body("Hello World"); } private void logMsg(final String msg) { log.info("Thread: {} {}", Thread.currentThread().getName(), msg); } }
观察到日志如下:
2024-09-02T16:04:56.072+05:30 INFO 68014 --- [nio-8080-exec-5] c.e.h.controller.HelloWorldController : Thread: http-nio-8080-exec-5 Hello Outside Body Thread 2024-09-02T16:04:56.072+05:30 INFO 68014 --- [pTaskExecutor11] c.e.h.controller.HelloWorldController : Thread: SpTaskExecutor11 Hello Inside Body Thread 2024-09-02T12:35:17.273+05:30 INFO 21749 --- [nio-8080-exec-4] c.e.h.controller.HelloWorldController : Thread: http-nio-8080-exec-4 Hello1
可以看到threadPoolTaskScheduler正在被使用,想了解getAsyncExecutor的预期使用场景是什么?
解答
一、两者的核心区别
定位与用途
getAsyncExecutor:是SpringAsyncConfigurer接口的方法,专门为**@Async注解标记的异步方法**提供默认执行器。它是Spring异步方法体系的核心配置入口,只负责普通异步任务的执行,不支持定时调度。ThreadPoolTaskScheduler:是Spring提供的兼具普通异步执行和定时/周期性任务调度能力的组件,既可以执行一次性异步任务,也能通过schedule系列方法实现定时任务,功能更全面。
绑定的场景
getAsyncExecutor:仅和@EnableAsync配合生效,所有未指定自定义执行器的@Async方法都会使用它返回的执行器。ThreadPoolTaskScheduler:可以作为通用的AsyncTaskExecutor被注入到任意需要异步执行的组件中(比如你配置里的Web MVC异步支持),同时支持定时任务的调度。
二、getAsyncExecutor的预期使用场景
从你的日志和配置来看,threadPoolTaskScheduler被绑定到了Web MVC的异步支持上,所以StreamingResponseBody的逻辑用了它,但getAsyncExecutor的作用场景是**@Async注解的异步方法**。
举个例子,如果你在服务类里写了这样的方法:
@Service public class AsyncService { @Async public void doAsyncWork() { log.info("执行异步任务,线程:{}", Thread.currentThread().getName()); } }
当调用这个方法时,就会使用你在AsyncConfig里通过getAsyncExecutor配置的my-sad-thread-xxx线程,而不是ThreadPoolTaskScheduler的线程。
简单说,getAsyncExecutor就是专门给@Async方法“量身定制”的默认执行器,只要你用了@EnableAsync且没给@Async指定其他执行器,它就会生效。
内容的提问来源于stack exchange,提问作者tuk
相关产品推荐
相关产品推荐

