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.5)

2025-04-24T13:04:51.596Z  INFO 128164 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.15 with PID 128164 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2025-04-24T13:04:51.610Z  INFO 128164 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2025-04-24T13:04:55.746Z  INFO 128164 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-04-24T13:04:55.802Z  INFO 128164 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-04-24T13:04:55.811Z  INFO 128164 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.40]
2025-04-24T13:04:56.023Z  INFO 128164 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-04-24T13:04:56.032Z  INFO 128164 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4166 ms
2025-04-24T13:04:58.277Z  INFO 128164 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2025-04-24T13:04:58.336Z  INFO 128164 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 9.643 seconds (process running for 11.397)
2025-04-24T13:04:58.369Z  INFO 128164 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2025-04-24T13:04:58.388Z  INFO 128164 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete

默认情况下,会显示 INFO 级别的日志消息,包括一些相关的启动详情,例如启动应用的用户。如果您需要除 INFO 之外的日志级别,可以进行设置,如 日志级别 中所述。应用版本是使用主应用类包中的实现版本确定的。通过将 spring.main.log-startup-info 设置为 false 可以关闭启动信息日志记录。这也会关闭应用活动 Profile 的日志记录。

要在启动期间添加额外的日志记录,您可以在 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 中可用的任何 key 以及以下任何占位符

表 1. Banner 变量
变量 描述

${application.version}

应用的版本号,如 MANIFEST.MF 中声明的。例如,Implementation-Version: 1.0 将打印为 1.0

${application.formatted-version}

应用的版本号,如 MANIFEST.MF 中声明的,并格式化用于显示(用括号括起来并以 v 开头)。例如 (v1.0)

${spring-boot.version}

您正在使用的 Spring Boot 版本。例如 3.4.5

${spring-boot.formatted-version}

您正在使用的 Spring Boot 版本,格式化用于显示(用括号括起来并以 v 开头)。例如 (v3.4.5)

${Ansi.NAME}(或 ${AnsiColor.NAME}${AnsiBackground.NAME}${AnsiStyle.NAME}

其中 NAME 是 ANSI 转义码的名称。详情请参见 AnsiPropertySource

${application.title}

应用的标题,如 MANIFEST.MF 中声明的。例如 Implementation-Title: MyApp 将打印为 MyApp

如果您想以编程方式生成 banner,可以使用 SpringApplication.setBanner(…​) 方法。使用 Banner 接口并实现您自己的 printBanner() 方法。

您还可以使用 spring.main.banner-mode 属性来确定 banner 是否应打印到 System.out (console)、发送到配置的 logger (log),或根本不生成 (off)。

打印的 banner 会以以下名称注册为单例 bean:springBootBanner

application.titleapplication.versionapplication.formatted-version 属性仅在使用 java -jar 或结合 Spring Boot 启动器使用 java -cp 时可用。如果您运行的是未打包的 jar 并使用 java -cp <classpath> <mainclass> 启动它,或将您的应用作为原生镜像运行,则这些值将无法解析。

要使用 application.* 属性,请使用 java -jar 将您的应用作为打包的 jar 启动,或使用 java org.springframework.boot.loader.launch.JarLauncher 将您的应用作为未打包的 jar 启动。这将在构建类路径和启动您的应用之前初始化 application.* banner 属性。

定制 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 允许您链接多个方法调用,并且包含 parentchild 方法,让您可以创建层次结构,如下例所示

  • 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 探针等基础设施向平台提供有关其可用性的信息。Spring Boot 内置支持常用的“活跃度 (liveness)”和“就绪度 (readiness)”可用性状态。如果您使用 Spring Boot 的 “actuator” 支持,这些状态将作为健康端点组暴露。

此外,您还可以通过将 ApplicationAvailability 接口注入到您自己的 bean 中来获取可用性状态。

活跃度状态

应用的“活跃度”状态表明其内部状态是否允许其正常工作,或者在当前失败时是否可以自行恢复。损坏的“活跃度”状态意味着应用处于无法恢复的状态,基础设施应该重启应用。

通常,“活跃度”状态不应基于外部检查,例如 健康检查。如果基于外部检查,一个失败的外部系统(数据库、Web API、外部缓存)将导致平台内大规模重启和级联故障。

Spring Boot 应用的内部状态主要由 Spring ApplicationContext 表示。如果应用上下文成功启动,Spring Boot 会认为应用处于有效状态。一旦上下文刷新完成,应用就被认为是活跃的,请参阅 Spring Boot 应用生命周期和相关的应用事件

就绪度状态

应用的“就绪度”状态表明应用是否已准备好处理流量。失败的“就绪度”状态告诉平台暂时不应将流量路由到该应用。这通常发生在启动期间,当 CommandLineRunnerApplicationRunner 组件正在处理时,或者在任何时候应用认为它太忙而无法处理额外流量时。

一旦应用和命令行运行器被调用,应用就被认为是就绪的,请参阅 Spring Boot 应用生命周期和相关的应用事件

期望在启动期间运行的任务应由 CommandLineRunnerApplicationRunner 组件执行,而不是使用 Spring 组件生命周期回调(例如 @PostConstruct)。

管理应用可用性状态

应用组件可以随时通过注入 ApplicationAvailability 接口并调用其方法来获取当前的可用性状态。更常见的是,应用会希望监听状态更新或更新应用状态。

例如,我们可以将应用的“就绪度”状态导出到文件中,以便 Kubernetes 的“exec 探针”可以查看该文件

  • 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 还会发送一些额外的应用事件。

某些事件实际上在 ApplicationContext 创建之前触发,因此您无法将其作为 @Bean 注册监听器。您可以使用 SpringApplication.addListeners(…​) 方法或 SpringApplicationBuilder.listeners(…​) 方法注册它们。

如果您希望这些监听器自动注册,无论应用以何种方式创建,您都可以向您的项目添加一个 META-INF/spring.factories 文件,并使用 ApplicationListener key 引用您的监听器,如下例所示

org.springframework.context.ApplicationListener=com.example.project.MyListener

应用事件在您的应用运行时按以下顺序发送

  1. 在运行开始时,但在注册监听器和初始化器之外的任何处理之前,发送 ApplicationStartingEvent

  2. 当上下文中使用的 Environment 已知,但在上下文创建之前,发送 ApplicationEnvironmentPreparedEvent

  3. ApplicationContext 准备好且 ApplicationContextInitializer 已被调用,但在加载任何 bean 定义之前,发送 ApplicationContextInitializedEvent

  4. 在刷新开始之前,但在 bean 定义加载之后,发送 ApplicationPreparedEvent

  5. 在上下文刷新之后,但在调用任何应用和命令行运行器之前,发送 ApplicationStartedEvent

  6. 紧随其后发送一个带有 LivenessState.CORRECTAvailabilityChangeEvent,表示应用被认为是活跃的。

  7. 在调用任何应用和命令行运行器之后,发送 ApplicationReadyEvent

  8. 紧随其后发送一个带有 ReadinessState.ACCEPTING_TRAFFICAvailabilityChangeEvent,表示应用已准备好处理请求。

  9. 如果启动时发生异常,发送 ApplicationFailedEvent

上述列表仅包含与 SpringApplication 相关的 SpringApplicationEvent。除此之外,在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前,还会发布以下事件

您通常不需要使用应用事件,但了解它们的存在可能会很有用。在内部,Spring Boot 使用事件来处理各种任务。
事件监听器不应运行潜在耗时的任务,因为它们默认在同一线程中执行。请考虑改用 应用和命令行运行器

应用事件使用 Spring Framework 的事件发布机制发送。该机制的一部分确保发布到子上下文监听器的事件也会发布到任何祖先上下文的监听器。因此,如果您的应用使用 SpringApplication 实例的层次结构,一个监听器可能会收到相同类型应用事件的多个实例。

为了让您的监听器区分其上下文的事件和后代上下文的事件,它应请求注入其应用上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware 或(如果监听器是一个 bean)使用 @Autowired 来注入上下文。

Web 环境

SpringApplication 会尝试为您创建正确类型的 ApplicationContext。用于确定 WebApplicationType 的算法如下

这意味着如果您在同一个应用程序中同时使用 Spring MVC 和来自 Spring WebFlux 的新 WebClient,默认将使用 Spring MVC。您可以通过调用 setWebApplicationType(WebApplicationType) 轻松覆盖此设置。

也可以通过调用 setApplicationContextFactory(…​) 完全控制所使用的 ApplicationContext 类型。

在 JUnit 测试中使用 SpringApplication 时,通常需要调用 setWebApplicationType(WebApplicationType.NONE)

访问应用程序参数

如果您需要访问传递给 SpringApplication.run(…​) 的应用程序参数,您可以注入一个 ApplicationArguments bean。ApplicationArguments 接口提供了对原始 String[] 参数以及解析后的 optionnon-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 启动后运行一些特定的代码,您可以实现 ApplicationRunnerCommandLineRunner 接口。这两个接口以相同的方式工作,并提供一个单一的 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...
	}

}

如果定义了多个 CommandLineRunnerApplicationRunner 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 属性,可以为应用程序启用与管理相关的功能。这会将 SpringApplicationAdminMXBean 暴露在平台 MBeanServer 上。您可以使用此功能远程管理您的 Spring Boot 应用程序。此功能对于任何服务包装器实现也可能有用。

如果您想知道应用程序正在哪个 HTTP 端口上运行,请获取键为 local.server.port 的属性。

应用程序启动跟踪

在应用程序启动期间,SpringApplicationApplicationContext 会执行许多与应用程序生命周期、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)
	}
}

Spring Framework 提供了第一个可用的实现 FlightRecorderApplicationStartup。它将 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 保持活动状态,即使所有线程都是虚拟线程。