跳到主要内容
版本:3.5.10

Task Execution and Scheduling

QWen Max 中英对照 Task Execution and Scheduling

在上下文中没有 Executor bean 的情况下,Spring Boot 会自动配置一个 AsyncTaskExecutor。当启用虚拟线程时(使用 Java 21+ 并将 spring.threads.virtual.enabled 设置为 true),该执行器将是一个使用虚拟线程的 SimpleAsyncTaskExecutor。否则,它将是一个具有合理默认值的 ThreadPoolTaskExecutor

除非定义了自定义的 Executor bean,否则以下集成将使用自动配置的 AsyncTaskExecutor

  • 使用 @EnableAsync 执行异步任务,除非定义了类型为 AsyncConfigurer 的 Bean。

  • 在 Spring for GraphQL 中,对控制器方法返回的 Callable 值进行异步处理。

  • Spring MVC 中的异步请求处理。

  • Spring WebFlux 中对阻塞式执行的支持。

  • 在 Spring WebSocket 中用于入站和出站消息通道。

  • 基于 JPA 仓库的引导模式,为 JPA 提供引导执行器。

  • 用于 ApplicationContext 中 Bean 的后台初始化的引导执行器。

虽然这种方法在大多数场景下都能正常工作,但 Spring Boot 允许你覆盖自动配置的 AsyncTaskExecutor。默认情况下,当注册了一个自定义的 Executor bean 时,自动配置的 AsyncTaskExecutor 会退让,而使用该自定义的 Executor 来执行常规任务(通过 @EnableAsync)。

然而,Spring MVC、Spring WebFlux 和 Spring GraphQL 都需要一个名为 applicationTaskExecutor 的 bean。对于 Spring MVC 和 Spring WebFlux,该 bean 必须是 AsyncTaskExecutor 类型,而 Spring GraphQL 则不强制要求此类型。

Spring WebSocket 和 JPA 将使用 AsyncTaskExecutor,前提是存在一个该类型的单例 Bean,或者定义了一个名为 applicationTaskExecutor 的 Bean。

最后,ApplicationContext 的 bootstrap 执行器会使用名为 applicationTaskExecutor 的 bean,除非定义了名为 bootstrapExecutor 的 bean。

以下代码片段演示了如何注册一个自定义的 AsyncTaskExecutor,以用于 Spring MVC、Spring WebFlux、Spring GraphQL、Spring WebSocket、JPA 以及 Bean 的后台初始化。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;

@Configuration(proxyBeanMethods = false)
public class MyTaskExecutorConfiguration {

@Bean("applicationTaskExecutor")
SimpleAsyncTaskExecutor applicationTaskExecutor() {
return new SimpleAsyncTaskExecutor("app-");
}

}
备注

如果应用上下文中不存在 @Primary 注解的 bean,或者没有名为 taskExecutor 且类型为 ExecutorAsyncConfigurer 的 bean,则 applicationTaskExecutor bean 也会用于常规任务的执行。

注意

如果既未自动配置 AsyncTaskExecutor,也未定义名为 applicationTaskExecutor 的 bean,则应用程序会默认使用名为 taskExecutor 的 bean 来执行常规任务(@EnableAsync),这遵循了 Spring Framework 的行为。然而,该 bean 不会被用于 Spring MVC、Spring WebFlux 或 Spring GraphQL。不过,如果该 bean 的类型是 AsyncTaskExecutor,则可能被用于 Spring WebSocket 或 JPA。

如果你的应用程序需要多个 Executor Bean 用于不同的集成场景,例如一个用于通过 @EnableAsync 进行常规任务执行,另一个用于 Spring MVC、Spring WebFlux、Spring WebSocket 和 JPA,你可以按如下方式配置它们。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration(proxyBeanMethods = false)
public class MyTaskExecutorConfiguration {

@Bean("applicationTaskExecutor")
SimpleAsyncTaskExecutor applicationTaskExecutor() {
return new SimpleAsyncTaskExecutor("app-");
}

@Bean("taskExecutor")
ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("async-");
return threadPoolTaskExecutor;
}

}
提示

自动配置的 ThreadPoolTaskExecutorBuilderSimpleAsyncTaskExecutorBuilder 允许你轻松创建 AsyncTaskExecutor 类型的实例,这些实例会复现自动配置的默认行为。

import org.springframework.boot.task.SimpleAsyncTaskExecutorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;

@Configuration(proxyBeanMethods = false)
public class MyTaskExecutorConfiguration {

@Bean
SimpleAsyncTaskExecutor taskExecutor(SimpleAsyncTaskExecutorBuilder builder) {
return builder.build();
}

}

如果名为 taskExecutor 的 bean 不可行,你可以将你的 bean 标记为 @Primary,或者定义一个 AsyncConfigurer bean,以指定负责处理使用 @EnableAsync 注解的常规任务执行的 Executor。以下示例演示了如何实现这一点。

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;

@Configuration(proxyBeanMethods = false)
public class MyTaskExecutorConfiguration {

@Bean
AsyncConfigurer asyncConfigurer(ExecutorService executorService) {
return new AsyncConfigurer() {

@Override
public Executor getAsyncExecutor() {
return executorService;
}

};
}

@Bean
ExecutorService executorService() {
return Executors.newCachedThreadPool();
}

}

要注册一个自定义的 Executor,同时保留自动配置的 AsyncTaskExecutor,你可以创建一个自定义的 Executor Bean,并在其 @Bean 注解中设置 defaultCandidate=false 属性,如下例所示:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyTaskExecutorConfiguration {

@Bean(defaultCandidate = false)
@Qualifier("scheduledExecutorService")
ScheduledExecutorService scheduledExecutorService() {
return Executors.newSingleThreadScheduledExecutor();
}

}

在这种情况下,你将能够将自定义的 Executor 自动装配到其他组件中,同时保留自动配置的 AsyncTaskExecutor。不过,请记住在使用 @Autowired 时,要同时使用 @Qualifier 注解。

如果这对你不可行,你仍然可以请求 Spring Boot 自动配置一个 AsyncTaskExecutor,如下所示:

spring.task.execution.mode=force

自动配置的 AsyncTaskExecutor 将自动用于所有集成,即使注册了自定义的 Executor Bean(包括标记为 @Primary 的 Bean)也是如此。这些集成包括:

  • 异步任务执行(@EnableAsync),除非存在一个 AsyncConfigurer bean。

  • Spring for GraphQL 对控制器方法中返回的 Callable 值的异步处理。

  • Spring MVC 的异步请求处理。

  • Spring WebFlux 的阻塞执行支持。

  • 用于 Spring WebSocket 中的入站和出站消息通道。

  • 基于 JPA 仓库的引导模式的 JPA 引导执行器。

  • 用于 ApplicationContext 中 Bean 的后台初始化的引导执行器,除非已定义名为 bootstrapExecutor 的 Bean。

提示

根据你的目标配置,你可以将 spring.task.execution.mode 设置为 force 以自动配置一个 applicationTaskExecutor,将你的 Executor 转换为 AsyncTaskExecutor,或者同时定义一个 AsyncTaskExecutor 和一个包装了你自定义 ExecutorAsyncConfigurer

注意

当启用 force 模式时,即使存在 @Primary 注解的 Bean 或名为 taskExecutor 且类型为 Executor 的 Bean,applicationTaskExecutor 也会被配置用于常规任务的执行(通过 @EnableAsync)。覆盖常规任务所用 Executor 的唯一方法是注册一个 AsyncConfigurer Bean。

当自动配置 ThreadPoolTaskExecutor 时,线程池使用 8 个核心线程,这些线程可以根据负载动态增长和缩减。这些默认设置可以通过 spring.task.execution 命名空间进行微调,如下例所示:

spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s

此更改使线程池使用有界队列,当队列已满(100 个任务)时,线程池会扩展至最多 16 个线程。线程池的收缩策略也更加激进,当线程空闲 10 秒后即被回收(默认为 60 秒)。

如果需要将调度器与定时任务执行相关联(例如使用 @EnableScheduling),也可以自动配置一个调度器。

如果启用了虚拟线程(使用 Java 21+ 并将 spring.threads.virtual.enabled 设置为 true),这将是一个使用虚拟线程的 SimpleAsyncTaskScheduler。该 SimpleAsyncTaskScheduler 将忽略任何与线程池相关的属性。

如果未启用虚拟线程,将使用一个具有合理默认值的 ThreadPoolTaskSchedulerThreadPoolTaskScheduler 默认使用一个线程,其设置可以通过 spring.task.scheduling 命名空间进行微调,如下例所示:

spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2

如果需要创建自定义的执行器(executor)或调度器(scheduler),上下文中会提供一个 ThreadPoolTaskExecutorBuilder bean、一个 SimpleAsyncTaskExecutorBuilder bean、一个 ThreadPoolTaskSchedulerBuilder bean 以及一个 SimpleAsyncTaskSchedulerBuilder。如果启用了虚拟线程(使用 Java 21+ 并将 spring.threads.virtual.enabled 设置为 true),则 SimpleAsyncTaskExecutorBuilderSimpleAsyncTaskSchedulerBuilder bean 会自动配置为使用虚拟线程。