任务执行与调度

如果上下文中没有 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 会让位,常规任务执行(通过 @EnableAsync)将使用自定义 Executor

但 Spring MVC、Spring WebFlux 和 Spring GraphQL 都要求存在名为 applicationTaskExecutor 的 bean。 对于 Spring MVC 和 Spring WebFlux,该 bean 必须为 AsyncTaskExecutor 类型,而 Spring GraphQL 不强制类型要求。

Spring WebSocket 和 JPA 会在存在单个 AsyncTaskExecutor 类型 bean 或定义了名为 applicationTaskExecutor 的 bean 时使用该类型。

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

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

  • Java

  • Kotlin

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-");
	}

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

@Configuration(proxyBeanMethods = false)
class MyTaskExecutorConfiguration {

	@Bean("applicationTaskExecutor")
	fun applicationTaskExecutor(): SimpleAsyncTaskExecutor {
		return SimpleAsyncTaskExecutor("app-")
	}

}

如果应用上下文中不存在 @Primary bean 或名为 taskExecutorExecutorAsyncConfigurer 类型 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 分别配置不同的执行器,可以如下配置。

  • Java

  • Kotlin

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;
	}

}
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)
class MyTaskExecutorConfiguration {

	@Bean("applicationTaskExecutor")
	fun applicationTaskExecutor(): SimpleAsyncTaskExecutor {
		return SimpleAsyncTaskExecutor("app-")
	}

	@Bean("taskExecutor")
	fun taskExecutor(): ThreadPoolTaskExecutor {
		val threadPoolTaskExecutor = ThreadPoolTaskExecutor()
		threadPoolTaskExecutor.setThreadNamePrefix("async-")
		return threadPoolTaskExecutor
	}

}

自动配置的 ThreadPoolTaskExecutorBuilderSimpleAsyncTaskExecutorBuilder 可帮助你轻松创建 AsyncTaskExecutor 实例,复现自动配置的默认行为。

  • Java

  • Kotlin

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();
	}

}
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)
class MyTaskExecutorConfiguration {

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

}

如果无法使用名为 taskExecutor 的 bean,可以将 bean 标记为 @Primary,或定义 AsyncConfigurer bean,指定用于常规任务执行(@EnableAsync)的 Executor。 以下示例演示了如何实现。

  • Java

  • Kotlin

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();
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.AsyncConfigurer
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

@Configuration(proxyBeanMethods = false)
class MyTaskExecutorConfiguration {

	@Bean
	fun asyncConfigurer(executorService: ExecutorService): AsyncConfigurer {
		return object : AsyncConfigurer {
			override fun getAsyncExecutor(): Executor {
				return executorService
			}
		}
	}

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

}

如需在保留自动配置的 AsyncTaskExecutor 的同时注册自定义 Executor,可创建自定义 Executor bean,并在其 @Bean 注解中设置 defaultCandidate=false,如下所示:

  • Java

  • Kotlin

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();
	}

}
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService

@Configuration(proxyBeanMethods = false)
class MyTaskExecutorConfiguration {

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

}

此时,你可以将自定义 Executor 自动注入到其他组件,同时保留自动配置的 AsyncTaskExecutor。 但请记得结合使用 @Qualifier@Autowired 注解。

如果无法满足上述条件,可以如下方式请求 Spring Boot 仍然自动配置 AsyncTaskExecutor

  • Properties

  • YAML

spring.task.execution.mode=force
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,或同时定义 AsyncTaskExecutorAsyncConfigurer,包装你的自定义 Executor

启用 force 模式后,即使存在 @Primary bean 或名为 taskExecutorExecutorapplicationTaskExecutor 也会被配置用于常规任务执行(@EnableAsync)。 唯一覆盖常规任务执行 Executor 的方式是注册 AsyncConfigurer bean。

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

  • Properties

  • YAML

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

这样配置后,线程池会使用有界队列,当队列满(100 个任务)时,线程池会扩展到最多 16 个线程。 线程池收缩更为激进,空闲线程 10 秒(默认 60 秒)后即被回收。

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

如果启用虚拟线程(Java 21+ 且 spring.threads.virtual.enabled 设置为 true),将使用基于虚拟线程的 SimpleAsyncTaskScheduler,该调度器会忽略所有与池相关的属性。

未启用虚拟线程时,将使用带有合理默认值的 ThreadPoolTaskScheduler。 该调度器默认使用一个线程,可通过 spring.task.scheduling 命名空间微调设置,如下所示:

  • Properties

  • YAML

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

如需自定义执行器或调度器,容器中会自动提供 ThreadPoolTaskExecutorBuilderSimpleAsyncTaskExecutorBuilderThreadPoolTaskSchedulerBuilderSimpleAsyncTaskSchedulerBuilder bean。 如启用虚拟线程,SimpleAsyncTaskExecutorBuilderSimpleAsyncTaskSchedulerBuilder bean 也会自动配置为使用虚拟线程(Java 21+ 且 spring.threads.virtual.enabled 设置为 true)。