任務執行與排程

如果在上下文中沒有 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 不強制執行此型別要求。

如果存在此型別的單個 bean 或定義了名為 applicationTaskExecutor 的 bean,Spring WebSocket 和 JPA 將使用 AsyncTaskExecutor

最後,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 或名為 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,您可以按如下方式配置它們。

  • 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。但是,請記住在 @Autowired 旁邊使用 @Qualifier 註解。

如果這對您來說不可能,您可以要求 Spring Boot 無論如何都自動配置一個 AsyncTaskExecutor,如下所示:

  • 屬性

  • YAML

spring.task.execution.mode=force
spring:
  task:
    execution:
      mode: force

自動配置的 AsyncTaskExecutor 將自動用於所有整合,即使註冊了自定義的 Executor bean,包括那些標記為 @Primary 的。這些整合包括:

  • 非同步任務執行(@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 名稱空間進行微調,如下例所示:

  • 屬性

  • 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。此 SimpleAsyncTaskScheduler 將忽略任何與池相關的屬性。

如果未啟用虛擬執行緒,它將是一個帶有合理預設值的 ThreadPoolTaskSchedulerThreadPoolTaskScheduler 預設使用一個執行緒,其設定可以使用 spring.task.scheduling 名稱空間進行微調,如下例所示:

  • 屬性

  • YAML

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

如果需要建立自定義執行器或排程器,上下文中將提供一個 ThreadPoolTaskExecutorBuilder bean、一個 SimpleAsyncTaskExecutorBuilder bean、一個 ThreadPoolTaskSchedulerBuilder bean 和一個 SimpleAsyncTaskSchedulerBuilder。如果啟用了虛擬執行緒(使用 Java 21+ 且 spring.threads.virtual.enabled 設定為 true),則 SimpleAsyncTaskExecutorBuilderSimpleAsyncTaskSchedulerBuilder bean 將自動配置為使用虛擬執行緒。

© . This site is unofficial and not affiliated with VMware.