SpringApplication
SpringApplication
类提供了一种便捷的方式来引导从 main()
方法启动的 Spring 应用程序。
在许多情况下,你可以委托给静态的 SpringApplication.run(Class, String…)
方法,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
当你的应用程序启动时,你应该看到类似于以下输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.6)
2025-05-26T11:12:40.078+08:00 INFO 1377 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 21.0.7 with PID 1377 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2025-05-26T11:12:40.079+08:00 INFO 1377 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default"
2025-05-26T11:12:40.400+08:00 INFO 1377 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-05-26T11:12:40.406+08:00 INFO 1377 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-05-26T11:12:40.406+08:00 INFO 1377 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.41]
2025-05-26T11:12:40.422+08:00 INFO 1377 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-05-26T11:12:40.423+08:00 INFO 1377 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 326 ms
2025-05-26T11:12:40.570+08:00 INFO 1377 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2025-05-26T11:12:40.574+08:00 INFO 1377 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 0.65 seconds (process running for 0.745)
2025-05-26T11:12:40.575+08:00 INFO 1377 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
默认情况下,会显示 INFO
级别的日志消息,包括一些相关的启动详细信息,例如启动应用程序的用户。
如果你需要除 INFO
以外的日志级别,可以按照 日志级别 中描述的方式进行设置。
应用程序版本是使用主应用程序类的包中的实现版本确定的。
可以通过将 spring.main.log-startup-info
设置为 false
来关闭启动信息日志记录。
这也会关闭应用程序活动配置文件的日志记录。
要在启动期间添加额外的日志记录,你可以在 SpringApplication 的子类中覆盖 logStartupInfo(boolean) 。
|
启动失败
如果你的应用程序启动失败,已注册的 FailureAnalyzer
bean 将有机会提供专门的错误消息和修复问题的具体操作。
例如,如果你在端口 8080
上启动 Web 应用程序,而该端口已被占用,你应该看到类似于以下消息:
***************************
APPLICATION FAILED TO START
***************************
Description:
Embedded servlet container failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
Spring Boot 提供了许多 FailureAnalyzer 实现,你可以 添加你自己的。
|
如果没有故障分析器能够处理异常,你仍然可以显示完整的条件报告以更好地了解出了什么问题。
为此,你需要 启用 debug
属性 或为 ConditionEvaluationReportLoggingListener
启用 DEBUG
日志记录。
例如,如果你使用 java -jar
运行应用程序,可以按如下方式启用 debug
属性:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
延迟初始化
SpringApplication
允许应用程序进行延迟初始化。
启用延迟初始化后,bean 会在需要时创建,而不是在应用程序启动期间。
因此,启用延迟初始化可以减少应用程序启动所需的时间。
在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 bean 在收到 HTTP 请求之前不会被初始化。
延迟初始化的一个缺点是它可能会延迟发现应用程序中的问题。 如果错误配置的 bean 被延迟初始化,故障将不会在启动期间发生,只有在 bean 被初始化时问题才会变得明显。 还必须确保 JVM 有足够的内存来容纳所有应用程序的 bean,而不仅仅是启动期间初始化的那些。 由于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前先调整 JVM 的堆大小。
可以使用 SpringApplicationBuilder
上的 lazyInitialization
方法或 SpringApplication
上的 setLazyInitialization
方法以编程方式启用延迟初始化。
或者,可以使用 spring.main.lazy-initialization
属性启用它,如下例所示:
-
Properties
-
YAML
spring.main.lazy-initialization=true
spring:
main:
lazy-initialization: true
如果你想在使用延迟初始化时对某些 bean 禁用延迟初始化,可以使用 @Lazy(false) 注解显式设置它们的 lazy 属性为 false。
|
自定义 Banner
可以通过在类路径中添加 banner.txt
文件或通过设置 spring.banner.location
属性指向此类文件的位置来更改启动时打印的 banner。
如果文件使用 UTF-8 以外的编码,可以设置 spring.banner.charset
。
在你的 banner.txt
文件中,你可以使用 Environment
中可用的任何键以及以下任何占位符:
变量 | 描述 |
---|---|
|
你的应用程序的版本号,在 |
|
你的应用程序的版本号,在 |
|
你使用的 Spring Boot 版本。
例如 |
|
你使用的 Spring Boot 版本,格式化显示(用括号包围并前缀为 |
|
其中 |
|
你的应用程序的标题,在 |
如果你想以编程方式生成 banner,可以使用 SpringApplication.setBanner(…) 方法。
使用 Banner 接口并实现你自己的 printBanner() 方法。
|
你还可以使用 spring.main.banner-mode
属性来确定是否必须在 System.out
(console
)上打印 banner,发送到配置的日志记录器(log
),或者根本不生成(off
)。
打印的 banner 作为单例 bean 注册在以下名称下:springBootBanner
。
如果使用 要使用 |
自定义 SpringApplication
如果 SpringApplication
的默认值不符合你的需求,你可以创建一个本地实例并自定义它。
例如,要关闭 banner,你可以这样写:
-
Java
-
Kotlin
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
}
}
传递给 SpringApplication 的构造函数参数是 Spring bean 的配置源。
在大多数情况下,这些是对 @Configuration 类的引用,但它们也可以是对 @Component 类的直接引用。
|
也可以使用 application.properties
文件配置 SpringApplication
。
有关详细信息,请参阅 外部化配置。
有关配置选项的完整列表,请参阅 SpringApplication
API 文档。
流式构建器 API
如果你需要构建 ApplicationContext
层次结构(具有父子关系的多个上下文)或者更喜欢使用流式构建器 API,你可以使用 SpringApplicationBuilder
。
SpringApplicationBuilder
允许你链接多个方法调用,并包含 parent
和 child
方法,让你创建层次结构,如下例所示:
-
Java
-
Kotlin
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
SpringApplicationBuilder()
.sources(Parent::class.java)
.child(Application::class.java)
.bannerMode(Banner.Mode.OFF)
.run(*args)
创建 ApplicationContext 层次结构时有一些限制。
例如,Web 组件*必须*包含在子上下文中,并且父上下文和子上下文使用相同的 Environment 。
有关完整详细信息,请参阅 SpringApplicationBuilder API 文档。
|
应用程序可用性
当部署在平台上时,应用程序可以使用 Kubernetes Probes 等基础设施向平台提供有关其可用性的信息。 Spring Boot 包含对常用的"活跃度"和"就绪度"可用性状态的现成支持。 如果你使用 Spring Boot 的"actuator"支持,这些状态会作为健康端点组公开。
此外,你还可以通过将 ApplicationAvailability
接口注入到你自己的 bean 中来获取可用性状态。
活跃度状态
应用程序的"活跃度"状态表明其内部状态是否允许其正常工作,或者在当前失败时自行恢复。 损坏的"活跃度"状态意味着应用程序处于无法恢复的状态,基础设施应该重启应用程序。
通常,"活跃度"状态不应基于外部检查,例如 健康检查。 如果这样做,失败的外部系统(数据库、Web API、外部缓存)将触发平台上的大规模重启和级联故障。 |
Spring Boot 应用程序的内部状态主要由 Spring ApplicationContext
表示。
如果应用程序上下文已成功启动,Spring Boot 假定应用程序处于有效状态。
一旦上下文被刷新,应用程序就被认为是活跃的,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
就绪度状态
应用程序的"就绪度"状态表明应用程序是否准备好处理流量。
失败的"就绪度"状态告诉平台现在不应该将流量路由到应用程序。
这通常发生在启动期间,当 CommandLineRunner
和 ApplicationRunner
组件正在处理时,或者在应用程序决定它太忙而无法处理额外流量的任何时候。
一旦应用程序和命令行运行器被调用,应用程序就被认为是就绪的,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
应该在启动期间运行的任务应该由 CommandLineRunner 和 ApplicationRunner 组件执行,而不是使用 Spring 组件生命周期回调,如 @PostConstruct 。
|
管理应用程序可用性状态
应用程序组件可以随时通过注入 ApplicationAvailability
接口并调用其方法来检索当前的可用性状态。
更常见的是,应用程序会希望监听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的"就绪度"状态导出到文件,以便 Kubernetes "exec Probe" 可以查看此文件:
-
Java
-
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyReadinessStateExporter {
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC -> {
// create file /tmp/healthy
}
case REFUSING_TRAFFIC -> {
// remove file /tmp/healthy
}
}
}
}
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.ReadinessState
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
@Component
class MyReadinessStateExporter {
@EventListener
fun onStateChange(event: AvailabilityChangeEvent<ReadinessState?>) {
when (event.state) {
ReadinessState.ACCEPTING_TRAFFIC -> {
// create file /tmp/healthy
}
ReadinessState.REFUSING_TRAFFIC -> {
// remove file /tmp/healthy
}
else -> {
// ...
}
}
}
}
我们还可以在应用程序损坏且无法恢复时更新应用程序的状态:
-
Java
-
Kotlin
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class MyLocalCacheVerifier {
private final ApplicationEventPublisher eventPublisher;
public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void checkLocalCache() {
try {
// ...
}
catch (CacheCompletelyBrokenException ex) {
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
}
}
}
import org.springframework.boot.availability.AvailabilityChangeEvent
import org.springframework.boot.availability.LivenessState
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component
@Component
class MyLocalCacheVerifier(private val eventPublisher: ApplicationEventPublisher) {
fun checkLocalCache() {
try {
// ...
} catch (ex: CacheCompletelyBrokenException) {
AvailabilityChangeEvent.publish(eventPublisher, ex, LivenessState.BROKEN)
}
}
}
Spring Boot 提供了 使用 Actuator 健康端点的 Kubernetes HTTP 探针用于"活跃度"和"就绪度"。 你可以在专用部分获取有关 在 Kubernetes 上部署 Spring Boot 应用程序 的更多指导。
应用程序事件和监听器
除了通常的 Spring Framework 事件(如 ContextRefreshedEvent
)之外,SpringApplication
还会发送一些额外的应用程序事件。
某些事件实际上是在 如果你希望无论应用程序的创建方式如何,这些监听器都能自动注册,你可以在项目中添加
|
随着应用程序的运行,应用程序事件按以下顺序发送:
-
在运行开始时但在任何处理之前(除了注册监听器和初始化器)发送
ApplicationStartingEvent
。 -
当要在上下文中使用的
Environment
已知但在创建上下文之前发送ApplicationEnvironmentPreparedEvent
。 -
当
ApplicationContext
已准备就绪且已调用 ApplicationContextInitializers 但在加载任何 bean 定义之前发送ApplicationContextInitializedEvent
。 -
在刷新开始之前但在加载 bean 定义之后发送
ApplicationPreparedEvent
。 -
在上下文已刷新但在调用任何应用程序和命令行运行器之前发送
ApplicationStartedEvent
。 -
紧接着发送
AvailabilityChangeEvent
,带有LivenessState.CORRECT
,表示应用程序被认为是活跃的。 -
在调用任何 应用程序和命令行运行器 之后发送
ApplicationReadyEvent
。 -
紧接着发送
AvailabilityChangeEvent
,带有ReadinessState.ACCEPTING_TRAFFIC
,表示应用程序已准备好处理请求。 -
如果在启动时发生异常,则发送
ApplicationFailedEvent
。
上面的列表只包括与 SpringApplication
相关的 SpringApplicationEvent
。
除此之外,在 ApplicationPreparedEvent
之后和 ApplicationStartedEvent
之前还会发布以下事件:
-
在
WebServer
准备就绪后发送WebServerInitializedEvent
。ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是 servlet 和响应式变体。 -
当
ApplicationContext
被刷新时发送ContextRefreshedEvent
。
你通常不需要使用应用程序事件,但知道它们存在可能会很有用。 在内部,Spring Boot 使用事件来处理各种任务。 |
监听器默认在同一线程中执行,因此不应运行可能耗时的任务。 考虑改用 应用程序和命令行运行器。 |
应用程序事件使用 Spring Framework 的事件发布机制发送。
此机制的一部分确保发布到子上下文中监听器的事件也会发布到任何祖先上下文中的监听器。
因此,如果你的应用程序使用 SpringApplication
实例的层次结构,监听器可能会收到同一类型应用程序事件的多个实例。
要允许你的监听器区分其上下文的事件和后代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。
可以通过实现 ApplicationContextAware
或如果监听器是 bean,则使用 @Autowired
来注入上下文。
Web 环境
SpringApplication
会尝试代表你创建正确类型的 ApplicationContext
。
用于确定 WebApplicationType
的算法如下:
-
如果存在 Spring MVC,则使用
AnnotationConfigServletWebServerApplicationContext
-
如果不存在 Spring MVC 但存在 Spring WebFlux,则使用
AnnotationConfigReactiveWebServerApplicationContext
这意味着如果你在同一应用程序中使用 Spring MVC 和 Spring WebFlux 的新 WebClient
,默认将使用 Spring MVC。
你可以通过调用 setWebApplicationType(WebApplicationType)
轻松覆盖它。
也可以通过调用 setApplicationContextFactory(…)
来完全控制使用的 ApplicationContext
类型。
在 JUnit 测试中使用 SpringApplication 时,通常需要调用 setWebApplicationType(WebApplicationType.NONE) 。
|
访问应用程序参数
如果你需要访问传递给 SpringApplication.run(…)
的应用程序参数,可以注入 ApplicationArguments
bean。
ApplicationArguments
接口提供对原始 String[]
参数以及已解析的 option
和 non-option
参数的访问,如下例所示:
-
Java
-
Kotlin
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
import org.springframework.boot.ApplicationArguments
import org.springframework.stereotype.Component
@Component
class MyBean(args: ApplicationArguments) {
init {
val debug = args.containsOption("debug")
val files = args.nonOptionArgs
if (debug) {
println(files)
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
Spring Boot 还使用 Spring Environment 注册 CommandLinePropertySource 。
这让你也可以使用 @Value 注解注入单个应用程序参数。
|
使用 ApplicationRunner 或 CommandLineRunner
如果你需要在 SpringApplication
启动后运行一些特定代码,可以实现 ApplicationRunner
或 CommandLineRunner
接口。
这两个接口的工作方式相同,都提供了一个 run
方法,该方法在 SpringApplication.run(…)
完成之前被调用。
这个契约非常适合应该在应用程序启动后但在开始接受流量之前运行的任务。 |
CommandLineRunner
接口提供对应用程序参数的字符串数组访问,而 ApplicationRunner
使用前面讨论的 ApplicationArguments
接口。
以下示例显示了一个带有 run
方法的 CommandLineRunner
:
-
Java
-
Kotlin
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component
@Component
class MyCommandLineRunner : CommandLineRunner {
override fun run(vararg args: String) {
// Do something...
}
}
如果定义了多个必须按特定顺序调用的 CommandLineRunner
或 ApplicationRunner
bean,你可以另外实现 Ordered
接口或使用 Order
注解。
应用程序退出
每个 SpringApplication
都会向 JVM 注册一个关闭钩子,以确保 ApplicationContext
在退出时正常关闭。
可以使用所有标准的 Spring 生命周期回调(如 DisposableBean
接口或 @PreDestroy
注解)。
此外,如果 bean 希望在调用 SpringApplication.exit()
时返回特定的退出代码,可以实现 ExitCodeGenerator
接口。
然后可以将此退出代码传递给 System.exit()
以将其作为状态代码返回,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
}
}
import org.springframework.boot.ExitCodeGenerator
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import kotlin.system.exitProcess
@SpringBootApplication
class MyApplication {
@Bean
fun exitCodeGenerator() = ExitCodeGenerator { 42 }
}
fun main(args: Array<String>) {
exitProcess(SpringApplication.exit(
runApplication<MyApplication>(*args)))
}
此外,ExitCodeGenerator
接口可以由异常实现。
当遇到此类异常时,Spring Boot 返回由实现的 getExitCode()
方法提供的退出代码。
如果有多个 ExitCodeGenerator
,则使用生成的第一个非零退出代码。
要控制生成器的调用顺序,另外实现 Ordered
接口或使用 Order
注解。
管理功能
可以通过指定 spring.application.admin.enabled
属性为应用程序启用管理相关功能。
这会在平台 MBeanServer
上公开 SpringApplicationAdminMXBean
。
你可以使用此功能远程管理你的 Spring Boot 应用程序。
此功能对于任何服务包装器实现也可能很有用。
如果你想知道应用程序在哪个 HTTP 端口上运行,请获取键为 local.server.port 的属性。
|
应用程序启动跟踪
在应用程序启动期间,SpringApplication
和 ApplicationContext
执行许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件相关的任务。
使用 ApplicationStartup
,Spring Framework 允许你使用 StartupStep
对象跟踪应用程序启动序列。
这些数据可以用于分析目的,或者只是为了更好地理解应用程序启动过程。
你可以在设置 SpringApplication
实例时选择 ApplicationStartup
实现。
例如,要使用 BufferingApplicationStartup
,你可以这样写:
-
Java
-
Kotlin
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
applicationStartup = BufferingApplicationStartup(2048)
}
}
第一个可用的实现 FlightRecorderApplicationStartup
由 Spring Framework 提供。
它将 Spring 特定的启动事件添加到 Java Flight Recorder 会话中,用于分析应用程序并将其 Spring 上下文生命周期与 JVM 事件(如分配、GC、类加载等)关联。
配置完成后,你可以通过启用 Flight Recorder 运行应用程序来记录数据:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar
Spring Boot 提供了 BufferingApplicationStartup
变体;此实现用于缓冲启动步骤并将它们排入外部指标系统。
应用程序可以在任何组件中请求类型为 BufferingApplicationStartup
的 bean。
Spring Boot 还可以配置为公开 startup
端点,以 JSON 文档形式提供此信息。
虚拟线程
如果你在 Java 21 或更高版本上运行,可以通过将 spring.threads.virtual.enabled
属性设置为 true
来启用虚拟线程。
在为此选项打开应用程序之前,你应该考虑 阅读官方 Java 虚拟线程文档。
在某些情况下,由于"Pinned Virtual Threads",应用程序可能会经历较低的吞吐量;该页面还解释了如何使用 JDK Flight Recorder 或 jcmd
CLI 检测此类情况。
如果启用了虚拟线程,配置线程池的属性将不再有效。 这是因为虚拟线程是在 JVM 范围的平台线程池上调度,而不是在专用线程池上。 |
虚拟线程的一个副作用是它们是守护线程。
如果 JVM 的所有线程都是守护线程,JVM 将退出。
当你依赖 @Scheduled bean 来保持应用程序存活时,这种行为可能会成为问题。
如果你使用虚拟线程,调度器线程是一个虚拟线程,因此是一个守护线程,不会保持 JVM 存活。
这不仅影响调度,也可能适用于其他技术。
为了在所有情况下保持 JVM 运行,建议将 spring.main.keep-alive 属性设置为 true 。
这确保即使所有线程都是虚拟线程,JVM 也会保持存活。
|