本文为官方文档直译版本。原文链接
SpringApplication
类为引导从 main()
方法启动的 Spring 应用程序提供了一种便捷的方法。在许多情况下,您可以委托静态 SpringApplication.run
方法,如下例所示:
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);
}
}
当应用程序启动时,您应该会看到与下面类似的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
2023-11-23T13:40:37.786Z INFO 39373 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.9 with PID 39373 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2023-11-23T13:40:37.791Z INFO 39373 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default"
2023-11-23T13:40:39.237Z INFO 39373 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2023-11-23T13:40:39.251Z INFO 39373 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-11-23T13:40:39.252Z INFO 39373 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.16]
2023-11-23T13:40:39.327Z INFO 39373 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-11-23T13:40:39.329Z INFO 39373 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1448 ms
2023-11-23T13:40:39.863Z INFO 39373 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
2023-11-23T13:40:39.876Z INFO 39373 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.652 seconds (process running for 3.034)
默认情况下,会显示 INFO
日志信息,包括一些相关的启动详细信息,如启动应用程序的用户。如果需要 INFO
以外的日志级别,可以按照日志级别中的说明进行设置。应用程序版本由主应用程序类包中的实现版本决定。可以通过将 spring.main.log-startup-info
设置为 false 关闭启动信息日志记录。这也将关闭应用程序活动配置文件的日志记录。
要在启动过程中添加额外的日志记录,可以在
SpringApplication
的子类中覆盖logStartupInfo(boolean)
。
如果应用程序启动失败,已注册的 FailureAnalyzers
会提供专门的错误消息和解决问题的具体操作。例如,如果您在 8080
端口上启动网络应用程序,而该端口已在使用中,您应该会看到与下面类似的消息:
***************************
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
实现,你也可以添加自己的实现。
如果没有故障分析器能够处理异常,您仍然可以显示完整的条件报告,以便更好地了解出错的原因。为此,您需要为 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
启用DEBUG
属性或启用 DEBUG
日志。
例如,如果您使用 java -jar
运行应用程序,可以按如下方式启用调试属性:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
SpringApplication
允许对应用程序进行懒初始化。启用懒初始化后,将根据需要创建 Bean,而不是在应用程序启动时创建。因此,启用懒初始化可以缩短应用程序的启动时间。在网络应用程序中,启用懒初始化将导致许多与网络相关的 Bean 在收到 HTTP 请求之前不会被初始化。
懒初始化的一个缺点是,它可能会延迟发现应用程序的问题。如果对配置错误的 Bean 进行了懒初始化,那么在启动过程中就不会再发生故障,而只有在初始化 Bean 时问题才会显现出来。此外,还必须注意确保 JVM 有足够的内存来容纳应用程序的所有 Bean,而不仅仅是那些在启动过程中初始化的 Bean。因此,默认情况下不会启用懒初始化,建议在启用懒初始化之前对 JVM 的堆大小进行微调。
可以使用 SpringApplicationBuilder
的 lazyInitialization
方法或 SpringApplication
的 setLazyInitialization
方法以编程方式启用懒初始化。或者,也可以使用 spring.main.lazy-initialization
属性启用懒初始化,如下例所示:
spring:
main:
lazy-initialization: true
如果您想对某些 Bean 禁用懒初始化,同时对应用程序的其他部分使用懒初始化,您可以使用
@Lazy(false)
注解显式地将它们的lazy
属性设置为false
。
可以通过在类路径中添加 banner.txt
文件或将 spring.banner.location
属性设置为此类文件的位置来更改启动时打印的横幅。如果文件的编码不是 UTF-8
,可以设置 spring.banner.charset
。
在 banner.txt
文件中,你可以使用环境中的任何可用键以及以下任何占位符:
Variable | Description |
---|---|
${application.version} |
MANIFEST.MF 中声明的应用程序版本号。例如,Implementation-Version: 1.0 打印为 1.0 。 |
${application.formatted-version} |
MANIFEST.MF 中声明的应用程序版本号,格式为显示格式(用括号包围,前缀为 v )。例如 (v1.0) 。 |
${spring-boot.version} |
您使用的 Spring Boot 版本。例如 3.2.0 。 |
${spring-boot.formatted-version} |
您正在使用的 Spring Boot 版本,显示格式为(用括号包围,前缀为 v )。例如(v3.2.0) 。 |
${Ansi.NAME} (or ${AnsiColor.NAME }, ${AnsiBackground.NAME} , ${AnsiStyle.NAME} ) |
其中 NAME 是 ANSI 转义代码的名称。详见 [AnsiPropertySource](https://github.com/spring-projects/spring-boot/tree/v3.2.0/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java) 。 |
${application.title} |
MANIFEST.MF 中声明的应用程序标题。例如,Implementation-Title: MyApp 打印为 MyApp 。 |
如果要以编程方式生成横幅,可以使用
SpringApplication.setBanner(...)
方法。使用org.springframework.boot.Banner
接口并实现自己的printBanner()
方法。
您还可以使用 spring.main.banner-mode
属性来决定是否将横幅打印在 System.out
上(控制台)、发送到配置的日志记录器上(log
)或完全不打印(off
)。
打印的横幅将作为单例 Bean 注册,名称如下:springBootBanner
。
application.title
、application.version
和application.formatted-version
属性只有在使用 Spring Boot 启动程序的java -jar
或java -cp
时可用。如果运行未打包的 jar 并使用java -cp
启动,或将应用程序作为本地映像运行,这些值将不会被解析。
要使用application.properties
,请使用java -jar
将应用程序作为打包的 jar 启动,或使用java org.springframework.boot.loader.launch.JarLauncher
将应用程序作为未打包的 jar 启动。这将在构建classpath
和启动应用程序之前初始化应用程序的banner
属性。
如果 SpringApplication
的默认设置不符合你的口味,你可以创建一个本地实例并对其进行自定义。例如,要关闭横幅,可以编写:
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);
}
}
传递给
SpringApplication
的构造函数参数是 Spring Bean 的配置源。大多数情况下,这些参数是对@Configuration
类的引用,但也可能是对@Component
类的直接引用。
也可以使用 application.properties
文件配置 SpringApplication
。详见外部化配置。
有关配置选项的完整列表,请参阅 SpringApplication Javadoc。
如果需要构建 ApplicationContext
层次结构(具有父/子关系的多个上下文),或者更喜欢使用 "fluent "的builder API,则可以使用 SpringApplicationBuilder
。
SpringApplicationBuilder
可让你将多个方法调用串联起来,并包含parent
和child
,让你创建一个层次结构,如下例所示:
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
创建
ApplicationContext
层次结构时有一些限制。例如,Web 组件必须包含在子上下文中,父上下文和子上下文使用相同的环境。有关详细信息,请参阅 SpringApplicationBuilder Javadoc 。
在平台上部署应用程序时,应用程序可以使用 Kubernetes Probes 等基础架构向平台提供有关其可用性的信息。Spring Boot 包括对常用 "liveness "和 "readiness "可用性状态的开箱即用支持。如果您使用的是 Spring Boot 的 "actuator "支持,那么这些状态将作为健康端点组公开。
此外,您还可以通过在自己的 Bean 中注入 ApplicationAvailability
接口来获取可用性状态。
应用程序的 “liveness” 状态说明其内部状态是否允许其正常工作,或在当前失败的情况下自行恢复。坏掉的 “liveness” 状态意味着应用程序处于无法恢复的状态,基础架构应重新启动应用程序。
一般来说,“liveness” 状态不应基于外部检查,如健康检查。如果这样做,外部系统(数据库、网络应用程序接口、外部缓存)的故障就会在整个平台上引发大规模重启和连锁故障。
Spring Boot 应用程序的内部状态主要由 Spring ApplicationContext
表示。如果应用程序上下文已成功启动,则 Spring Boot 认为应用程序处于有效状态。一旦上下文被刷新,应用程序即被视为已激活,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
应用程序的 “readiness” 状态说明应用程序是否已准备好处理流量。失败的 “readiness” 状态会告诉平台暂时不应将流量路由到应用程序。这种情况通常发生在启动过程中、CommandLineRunner
和 ApplicationRunner
组件处理过程中,或者在应用程序认为自己忙于处理额外流量时随时发生。
一旦应用程序和命令行运行程序被调用,应用程序即被视为准备就绪,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。
预计在启动过程中运行的任务应由
CommandLineRunner
和ApplicationRunner
组件执行,而不是使用 Spring 组件生命周期回调(如@PostConstruct
)。
应用程序组件可以通过注入 ApplicationAvailability
接口并调用其中的方法,随时获取当前的可用性状态。更常见的情况是,应用程序希望监听状态更新或更新应用程序的状态。
例如,我们可以将应用程序的 “readiness” 状态导出到文件中,这样 Kubernetes 的 “exec Probe”(执行探针) 就可以查看该文件:
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.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);
}
}
}
Spring Boot 通过 Actuator Health Endpoints 为 "Liveness "和 "Readiness "提供了 Kubernetes HTTP 探测器。有关在 Kubernetes 上部署 Spring Boot 应用程序的更多指导,请参阅专门章节。
除了常规的 Spring Framework 事件(如 [ContextRefreshedEvent](https://docs.spring.io/spring-framework/docs/6.1.1/javadoc-api/org/springframework/context/event/ContextRefreshedEvent.html)
)外,SpringApplication
还会发送一些额外的应用程序事件。
有些事件实际上是在创建
ApplicationContext
之前触发的,因此无法以@Bean
的形式为这些事件注册监听器。你可以使用SpringApplication.addListeners(...)
方法或SpringApplicationBuilder.listeners(...)
方法注册它们。
如果希望无论以何种方式创建应用程序都能自动注册监听器,可以在项目中添加META-INF/spring.factories
文件,并使用org.springframework.context.ApplicationListener
关键字引用监听器,如下例所示:
org.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件在运行过程中按以下顺序发送:
ApplicationStartingEvent
在运行开始时发送,但在进行任何处理(注册监听器和初始化器除外)之前发送。ApplicationEnvironmentPreparedEvent
(应用环境准备事件)。ApplicationContext
已准备就绪且 ApplicationContextInitializers
已被调用,但尚未加载任何 Bean
定义时,将发送 ApplicationContextInitializedEvent
。ApplicationPreparedEvent
会在刷新开始前、Bean 定义加载后发送。ApplicationStartedEvent
会在上下文刷新后但在调用任何应用程序和命令行运行程序前发送。LivenessState.CORRECT
的 AvailabilityChangeEvent
(可用性变更事件),以表明应用程序被认为是实时的。ApplicationReadyEvent
(应用程序就绪事件)。ReadinessState.ACCEPTING_TRAFFIC
的 AvailabilityChangeEvent
,表明应用程序已准备好为请求提供服务。ApplicationFailedEvent
。上述列表仅包括与 SpringApplication
绑定的 SpringApplicationEvents
。除此之外,以下事件也会在 ApplicationPreparedEvent
之后、ApplicationStartedEvent
之前发布:
WebServerInitializedEvent
会在 WebServer
就绪后发送。ServletWebServerInitializedEvent
和 ReactiveWebServerInitializedEvent
分别是 Servlet 和反应式的变体。ApplicationContext
时,会发送 ContextRefreshedEvent
。您通常不需要使用应用程序事件,但知道它们的存在可能会很方便。在内部,Spring Boot 使用事件来处理各种任务。
事件侦听器不应运行潜在的冗长任务,因为它们默认在同一线程中执行。请考虑使用应用程序和命令行运行程序。
应用程序事件是通过 Spring Framework 的事件发布机制发送的。该机制的部分功能是确保向子上下文中的监听器发布的事件也会向任何祖先上下文中的监听器发布。因此,如果您的应用程序使用 SpringApplication
实例的层次结构,监听器可能会接收到同一类型应用程序事件的多个实例。
为使监听器能够区分其上下文的事件和后代上下文的事件,监听器应请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware
或(如果监听器是一个 Bean)使用 @Autowired
来注入上下文。
SpringApplication
会尝试代表你创建正确类型的 ApplicationContext
。用于确定 WebApplicationType
的算法如下:
AnnotationConfigServletWebServerApplicationContext
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[]
参数以及解析后的选项和非选项参数的访问,如下例所示:
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"]
}
}
Spring Boot 还会在 Spring
Environment
中注册一个CommandLinePropertySource
。这样,您就可以使用@Value
注解注入单个应用程序参数。
如果需要在 SpringApplication
启动后运行某些特定代码,可以实现 ApplicationRunner
或 CommandLineRunner
接口。这两个接口的工作方式相同,都提供一个运行方法,在 SpringApplication.run(...)
完成之前调用。
这种约定非常适合在应用程序启动后、开始接受流量前运行的任务。
CommandLineRunner
接口以字符串数组的形式访问应用程序参数,而 ApplicationRunner
则使用前面讨论过的 ApplicationArguments
接口。下面的示例显示了带有run
方法的 CommandLineRunner
:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
如果定义了多个必须按特定顺序调用的 CommandLineRunner
或 ApplicationRunner
Bean,可以额外实现 org.springframework.core.Ordered
接口或使用 org.springframework.core.annotation.Order
注解。
每个 SpringApplication
都会向 JVM 注册一个关闭钩子,以确保 ApplicationContext
在退出时优雅地关闭。所有标准的 Spring 生命周期回调(如 DisposableBean
接口或 @PreDestroy
注解)均可使用。
此外,如果希望在调用 SpringApplication.exit()
时返回特定的退出代码,可以实现 org.springframework.boot.ExitCodeGenerator
接口。然后,可将此退出代码传递给 System.exit()
,使其作为状态代码返回,如下例所示:
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)));
}
}
此外,ExitCodeGenerator
接口也可以通过异常来实现。遇到此类异常时,Spring Boot 会返回由已实现的 getExitCode()
方法提供的退出代码。
如果存在多个 ExitCodeGenerator
,则使用最先生成的非零退出代码。要控制生成器的调用顺序,可额外实现 org.springframework.core.Ordered
接口或使用 org.springframework.core.annotation.Order
注解。
通过指定 spring.application.admin.enabled
属性,可以启用应用程序的管理相关功能。这将在平台 MBeanServer
上公开 [SpringApplicationAdminMXBean](https://github.com/spring-projects/spring-boot/blob/v3.2.0/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/admin/SpringApplicationAdminMXBean.java)
。您可以使用此功能远程管理 Spring Boot 应用程序。该功能对于任何服务包装器的实现也很有用。
如果想知道应用程序在哪个 HTTP 端口上运行,请获取键值为
local.server.port
的属性。
在应用程序启动期间,SpringApplication
和 ApplicationContext
会执行许多与应用程序生命周期、Bean 生命周期甚至处理应用程序事件相关的任务。通过 [ApplicationStartup](https://docs.spring.io/spring-framework/docs/6.1.1/javadoc-api/org/springframework/core/metrics/ApplicationStartup.html)
,Spring Framework 允许您使用 StartupStep
对象跟踪应用程序的启动顺序。收集这些数据可用于分析目的,或只是为了更好地了解应用程序的启动过程。
在设置 SpringApplication
实例时,可以选择 ApplicationStartup
实现。例如,要使用 BufferingApplicationStartup
,你可以这样写:
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);
}
}
第一个可用实现 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 也可以配置为公开一个启动端点,以 JSON 文档的形式提供此信息。
如果运行的是 Java 21 或更高版本,可以通过将属性 spring.threads.virtual.enabled
设置为 true
来启用虚拟线程。
虚拟线程的一个副作用是,这些线程都是守护线程。如果没有非守护线程,JVM 就会退出。当您依赖
@Scheduled
beans 等来保持应用程序的活力时,这种行为可能会造成问题。如果使用虚拟线程,调度线程就是一个虚拟线程,因此也是一个守护线程,不会让 JVM 继续运行。这不仅会影响调度,其他技术也会出现这种情况!为了在任何情况下都能保持 JVM 运行,建议将属性spring.main.keep-alive
设置为true
。这样,即使所有线程都是虚拟线程,也能确保 JVM 正常运行。