SpringApplication

SpringApplication 类提供了一种便捷的方式来引导一个从 main() 方法启动的 Spring 应用。在许多情况下,您可以委托给静态 SpringApplication.run 方法,如下例所示

  • 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.3.5)

2024-10-24T12:03:05.386Z  INFO 112801 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.13 with PID 112801 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2024-10-24T12:03:05.406Z  INFO 112801 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2024-10-24T12:03:08.644Z  INFO 112801 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-10-24T12:03:08.685Z  INFO 112801 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-10-24T12:03:08.690Z  INFO 112801 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.31]
2024-10-24T12:03:08.898Z  INFO 112801 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-10-24T12:03:08.900Z  INFO 112801 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3244 ms
2024-10-24T12:03:10.275Z  INFO 112801 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-10-24T12:03:10.290Z  INFO 112801 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 5.964 seconds (process running for 7.025)

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

要在启动期间添加其他日志记录,您可以在 SpringApplication 的子类中覆盖 logStartupInfo(boolean)

启动失败

如果您的应用启动失败,注册的 FailureAnalyzers 将有机会提供专门的错误消息和解决问题的具体操作。例如,如果您在端口 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 属性org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener 启用 DEBUG 日志记录

例如,如果您正在使用 java -jar 运行您的应用,您可以按如下方式启用 debug 属性

$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug

延迟初始化

SpringApplication 允许应用程序延迟初始化。启用延迟初始化后,Bean 会在需要时创建,而不是在应用程序启动期间创建。因此,启用延迟初始化可以缩短应用程序启动时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 Bean 延迟到接收到 HTTP 请求时才初始化。

延迟初始化的一个缺点是它可能会延迟发现应用程序问题。如果错误配置的 Bean 是延迟初始化的,则故障将不再在启动时发生,而问题只有在 Bean 初始化时才会变得明显。还必须注意确保 JVM 拥有足够的内存来容纳所有应用程序的 Bean,而不仅仅是在启动期间初始化的那些 Bean。出于这些原因,默认情况下不会启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。

可以使用 SpringApplicationBuilder 上的 lazyInitialization 方法或 SpringApplication 上的 setLazyInitialization 方法以编程方式启用延迟初始化。或者,可以使用 spring.main.lazy-initialization 属性启用它,如下例所示

  • 属性

  • 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 中可用的任何键以及以下任何占位符

表 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.3.5

${spring-boot.formatted-version}

您正在使用的 Spring Boot 版本,格式化以供显示(用括号括起来并在前面加上 v)。例如 (v3.3.5)

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

其中 NAME 是 ANSI 转义代码的名称。有关详细信息,请参阅 AnsiPropertySource

${application.title}

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

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

您还可以使用 spring.main.banner-mode 属性来确定是否必须在 System.outconsole)上打印 Banner,发送到已配置的日志记录器(log),或者根本不生成 Banner(off)。

打印的 Banner 作为单例 Bean 注册,名称如下:springBootBanner

仅当您使用 java -jarjava -cp 以及 Spring Boot 启动器时,application.titleapplication.versionapplication.formatted-version 属性才可用。如果您正在运行解压的 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 文档。

Fluent Builder API

如果您需要构建 ApplicationContext 层次结构(具有父子关系的多个上下文),或者您更喜欢使用 Fluent Builder 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 包括对常用的“存活性”和“就绪性”可用性状态的开箱即用支持。如果您使用 Spring Boot 的“执行器”支持,则这些状态将作为健康端点组公开。

此外,您还可以通过将 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 框架事件(例如 ContextRefreshedEvent)之外,SpringApplication 还会发送一些其他应用程序事件。

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

如果希望无论应用程序创建方式如何都自动注册这些侦听器,则可以在项目中添加 META-INF/spring.factories 文件,并使用 org.springframework.context.ApplicationListener 键引用您的侦听器,如下例所示

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

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

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

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

  3. 在准备 ApplicationContext 并且已调用 ApplicationContextInitializers 但在加载任何 Bean 定义之前发送 ApplicationContextInitializedEvent

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

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

  6. 之后发送 AvailabilityChangeEvent,并使用 LivenessState.CORRECT 指示应用程序被视为存活。

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

  8. 之后发送 AvailabilityChangeEvent,并使用 ReadinessState.ACCEPTING_TRAFFIC 指示应用程序已准备好为请求提供服务。

  9. 如果启动时出现异常,则发送 ApplicationFailedEvent

以上列表仅包含与 SpringApplication 绑定的 SpringApplicationEvent。除了这些之外,还在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发布以下事件

  • WebServer 准备好后发送 WebServerInitializedEventServletWebServerInitializedEventReactiveWebServerInitializedEvent 分别是 servlet 和反应式变体。

  • ApplicationContext 刷新时发送 ContextRefreshedEvent

您通常不需要使用应用程序事件,但了解它们的存在会很有帮助。在内部,Spring Boot 使用事件来处理各种任务。

事件监听器不应该运行潜在的长时间任务,因为它们默认在同一个线程中执行。请考虑改用应用程序和命令行运行器

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

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

Web 环境

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

  • 如果存在 Spring MVC,则使用AnnotationConfigServletWebServerApplicationContext

  • 如果不存在 Spring MVC 且存在 Spring WebFlux,则使用AnnotationConfigReactiveWebServerApplicationContext

  • 否则,使用AnnotationConfigApplicationContext

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

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

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

访问应用程序参数

如果您需要访问传递给SpringApplication.run(…​)的应用程序参数,则可以注入一个org.springframework.boot.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,则还可以额外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。

应用程序退出

每个SpringApplication都会在 JVM 中注册一个关闭钩子,以确保ApplicationContext在退出时正常关闭。可以使用所有标准的 Spring 生命周期回调(例如DisposableBean接口或@PreDestroy注解)。

此外,如果 Bean 希望在调用SpringApplication.exit()时返回特定的退出代码,则可以实现org.springframework.boot.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,则使用生成的第一个非零退出代码。要控制生成器调用的顺序,请另外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。

管理功能

可以通过指定spring.application.admin.enabled属性为应用程序启用与管理相关的功能。这会在平台MBeanServer上公开SpringApplicationAdminMXBean。您可以使用此功能远程管理您的 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 虚拟线程文档。在某些情况下,应用程序可能会因“固定虚拟线程”而导致吞吐量降低;此页面还说明了如何使用 JDK Flight Recorder 或jcmd CLI 检测此类情况。

如果启用了虚拟线程,则配置线程池的属性将不再生效。这是因为虚拟线程是在 JVM 范围内的平台线程池上调度,而不是在专用线程池上调度。
虚拟线程的一个副作用是它们是守护线程。如果其所有线程都是守护线程,则 JVM 将退出。例如,当您依赖@Scheduled Bean 来保持应用程序活动时,此行为可能会成为问题。如果使用虚拟线程,则调度程序线程是虚拟线程,因此是守护线程,不会使 JVM 保持活动状态。这不仅会影响调度,而且在其他技术中也可能出现这种情况。为了在所有情况下都保持 JVM 运行,建议将属性spring.main.keep-alive设置为true。这确保了即使所有线程都是虚拟线程,JVM 也会保持活动状态。