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

2024-05-23T12:38:27.526Z  INFO 111218 --- [           main] o.s.b.d.f.logexample.MyApplication       : Starting MyApplication using Java 17.0.11 with PID 111218 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2024-05-23T12:38:27.544Z  INFO 111218 --- [           main] o.s.b.d.f.logexample.MyApplication       : No active profile set, falling back to 1 default profile: "default"
2024-05-23T12:38:31.099Z  INFO 111218 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-05-23T12:38:31.132Z  INFO 111218 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-05-23T12:38:31.132Z  INFO 111218 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.24]
2024-05-23T12:38:31.282Z  INFO 111218 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-05-23T12:38:31.286Z  INFO 111218 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3481 ms
2024-05-23T12:38:32.789Z  INFO 111218 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-05-23T12:38:32.831Z  INFO 111218 --- [           main] o.s.b.d.f.logexample.MyApplication       : Started MyApplication in 7.198 seconds (process running for 8.944)

默认情况下,会显示 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) 注解显式地将它们的延迟属性设置为 false。

自定义横幅

可以在启动时打印的横幅可以通过向类路径添加 banner.txt 文件或将 spring.banner.location 属性设置为此类文件的位置来更改。如果文件具有除 UTF-8 之外的编码,则可以设置 spring.banner.charset

banner.txt 文件中,可以使用 Environment 中的任何键以及以下任何占位符

表 1. 横幅变量
变量 说明

${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.0

${spring-boot.formatted-version}

您正在使用的 Spring Boot 版本,并格式化为显示(用括号括起来,并以 v 为前缀)。例如 (v3.3.0)

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

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

${application.title}

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

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

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

打印的横幅以以下名称注册为单例 bean:springBootBanner

application.titleapplication.versionapplication.formatted-version 属性仅在您将 java -jarjava -cp 与 Spring Boot 启动器一起使用时可用。如果您正在运行未打包的 jar 并使用 java -cp <classpath> <mainclass> 启动它或将应用程序作为本机映像运行,则不会解析这些值。

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

自定义 SpringApplication

如果 SpringApplication 默认值不符合您的喜好,您可以创建一个本地实例并对其进行自定义。例如,要关闭横幅,您可以编写

  • 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 Javadoc

流畅的构建器 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 Javadoc

应用程序可用性

在平台上部署时,应用程序可以使用诸如 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 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 框架事件,如 ContextRefreshedEventSpringApplication 还会发送一些其他应用程序事件。

某些事件实际上是在创建 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. ApplicationEnvironmentPreparedEvent 在上下文中使用的 Environment 已知但上下文尚未创建时发送。

  3. ApplicationContextInitializedEventApplicationContext 准备就绪且已调用 ApplicationContextInitializers 但在加载任何 bean 定义之前发送。

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

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

  6. AvailabilityChangeEventLivenessState.CORRECT 之后立即发送,以表明应用程序被视为活动状态。

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

  8. AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFIC 之后立即发送,以表明应用程序已准备好处理请求。

  9. ApplicationFailedEvent 在启动时出现异常时发送。

以上列表仅包括与 SpringApplication 绑定的 SpringApplicationEvent。除此之外,以下事件也在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发布

  • WebServerInitializedEventWebServer 准备就绪后发送。ServletWebServerInitializedEventReactiveWebServerInitializedEvent 分别是 servlet 和响应式变体。

  • ContextRefreshedEventApplicationContext 刷新时发送。

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

应用程序事件是使用 Spring Framework 的事件发布机制发送的。此机制的一部分确保发布到子上下文中侦听器的事件也发布到任何祖先上下文中侦听器。因此,如果您的应用程序使用 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 将退出。当您依赖 @Scheduled bean(例如,让您的应用程序保持活动状态)时,此行为可能存在问题。如果您使用虚拟线程,则调度程序线程是虚拟线程,因此是守护线程,并且不会让 JVM 保持活动状态。这不仅会影响调度,而且也会影响其他技术。为了在所有情况下都让 JVM 运行,建议将属性 spring.main.keep-alive 设置为 true。这可确保 JVM 保持活动状态,即使所有线程都是虚拟线程也是如此。