SpringApplication中文文档

本文为官方文档直译版本。原文链接

SpringApplication中文文档

    • 引言
    • 启动失败
    • 懒惰式初始化
    • 自定义横幅(Banner)
    • 自定义 SpringApplication
    • 流畅构建API
    • 应用程序的可用性
      • 运行状态
      • 就绪状态
      • 管理应用程序可用状态
    • 应用程序事件和监听器
    • Web 环境
    • 访问应用程序参数
    • 使用 ApplicationRunner 或 CommandLineRunner
    • 退出应用程序
    • 管理功能
    • 跟踪应用程序启动
      • 虚拟线程

引言

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 的堆大小进行微调。
可以使用 SpringApplicationBuilderlazyInitialization 方法或 SpringApplicationsetLazyInitialization 方法以编程方式启用懒初始化。或者,也可以使用 spring.main.lazy-initialization 属性启用懒初始化,如下例所示:

spring:
  main:
    lazy-initialization: true

如果您想对某些 Bean 禁用懒初始化,同时对应用程序的其他部分使用懒初始化,您可以使用 @Lazy(false) 注解显式地将它们的 lazy 属性设置为 false

自定义横幅(Banner)

可以通过在类路径中添加 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}) 其中 NAMEANSI 转义代码的名称。详见 [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.titleapplication.versionapplication.formatted-version 属性只有在使用 Spring Boot 启动程序的 java -jarjava -cp 时可用。如果运行未打包的 jar 并使用 java -cp 启动,或将应用程序作为本地映像运行,这些值将不会被解析。
要使用application.properties,请使用 java -jar 将应用程序作为打包的 jar 启动,或使用 java org.springframework.boot.loader.launch.JarLauncher 将应用程序作为未打包的 jar 启动。这将在构建 classpath 和启动应用程序之前初始化应用程序的 banner 属性。

自定义 SpringApplication

如果 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。

流畅构建API

如果需要构建 ApplicationContext 层次结构(具有父/子关系的多个上下文),或者更喜欢使用 "fluent "的builder API,则可以使用 SpringApplicationBuilder
SpringApplicationBuilder 可让你将多个方法调用串联起来,并包含parentchild,让你创建一个层次结构,如下例所示:

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” 状态会告诉平台暂时不应将流量路由到应用程序。这种情况通常发生在启动过程中、CommandLineRunnerApplicationRunner 组件处理过程中,或者在应用程序认为自己忙于处理额外流量时随时发生。
一旦应用程序和命令行运行程序被调用,应用程序即被视为准备就绪,请参阅 Spring Boot 应用程序生命周期和相关应用程序事件。

预计在启动过程中运行的任务应由 CommandLineRunnerApplicationRunner 组件执行,而不是使用 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

应用程序事件在运行过程中按以下顺序发送:

  1. ApplicationStartingEvent 在运行开始时发送,但在进行任何处理(注册监听器和初始化器除外)之前发送。
  2. 当上下文中使用的环境已知,但还未创建上下文时,将发送 ApplicationEnvironmentPreparedEvent(应用环境准备事件)。
  3. ApplicationContext 已准备就绪且 ApplicationContextInitializers 已被调用,但尚未加载任何 Bean 定义时,将发送 ApplicationContextInitializedEvent
  4. ApplicationPreparedEvent 会在刷新开始前、Bean 定义加载后发送。
  5. ApplicationStartedEvent 会在上下文刷新后但在调用任何应用程序和命令行运行程序前发送。
  6. 紧接着会发送一个带有 LivenessState.CORRECTAvailabilityChangeEvent(可用性变更事件),以表明应用程序被认为是实时的。
  7. 应用程序和命令行运行程序被调用后,将发送 ApplicationReadyEvent(应用程序就绪事件)。
  8. 紧接着就会发送一个带有 ReadinessState.ACCEPTING_TRAFFICAvailabilityChangeEvent,表明应用程序已准备好为请求提供服务。
  9. 如果启动时出现异常,则会发送 ApplicationFailedEvent

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

  • WebServerInitializedEvent 会在 WebServer 就绪后发送。ServletWebServerInitializedEventReactiveWebServerInitializedEvent 分别是 Servlet 和反应式的变体。
  • 当刷新 ApplicationContext 时,会发送 ContextRefreshedEvent

您通常不需要使用应用程序事件,但知道它们的存在可能会很方便。在内部,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[] 参数以及解析后的选项和非选项参数的访问,如下例所示:

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 注解注入单个应用程序参数。

使用 ApplicationRunner 或 CommandLineRunner

如果需要在 SpringApplication 启动后运行某些特定代码,可以实现 ApplicationRunnerCommandLineRunner 接口。这两个接口的工作方式相同,都提供一个运行方法,在 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...
    }

}

如果定义了多个必须按特定顺序调用的 CommandLineRunnerApplicationRunner 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 的属性。

跟踪应用程序启动

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

你可能感兴趣的:(spring,boot,java,spring,boot,后端)