23、SpringApplication

SpringApplication 类提供了一种可通过运行 main() 方法来启动 Spring 应用的简单方式。多数情况下,您只需要委托给静态的 SpringApplication.run 方法:

  1. public static void main(String[] args) {
  2. SpringApplication.run(MySpringConfiguration.class, args);
  3. }

当应用启动时,您应该会看到类似以下的内容输出:

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: v2.1.1.RELEASE
  8. 2013-07-31 00:08:16.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb)
  9. 2013-07-31 00:08:16.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy
  10. 2014-03-04 13:09:54.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080
  11. 2014-03-04 13:09:56.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)

默认情况下,将显示 INFO 级别的日志信息,包括一些应用启动相关信息。如果您需要修改 INFO 日志级别,请参考 26.4 部分:日志等级

23.1、启动失败

如果您的应用无法启动,注册的 FailureAnalyzers 可能会提供有相关的错误信息和解决问题的具体方法。例如,如果您在已经被占用的 8080 端口上启动了一个 web 应用,会看到类似以下的错误信息:

  1. ***************************
  2. APPLICATION FAILED TO START
  3. ***************************
  4. Description:
  5. Embedded servlet container failed to start. Port 8080 was already in use.
  6. Action:
  7. Identify and stop the process that's 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 运行应用,可以按以下方式启用 debug 属性:

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

23.2、自定义 banner

可以通过在 classpath 下添加一个 banner.txt 文件,或者将 spring.banner.location 属性指向该文件的位置来更改启动时打印的 banner。如果文件采用了非 UTF-8 编码,您可以设置 spring.banner.charset 来解决。除了文本文件,您还可以将 banner.gifbanner.jpg 或者 banner.png 图片文件添加到 classpath 下,或者设置 spring.banner.image.location 属性。指定的图片将会被转换成 ASCII 形式并打印在 banner 文本上方。

您可以在 banner.txt 文件中使用以下占位符:

变量 描述
${application.version} 您的应用版本号,声明在 MANIFEST.MF 中。例如,Implementation-Version: 1.0 将被打印为 1.0
${application.formatted-version} 您的应用版本号,声明在 MANIFEST.MF 中,格式化之后打印(用括号括起来,以 v 为前缀),例如 (v1.0)。
${spring-boot.version} 您使用的 Spring Boot 版本。例如 2.1.1.RELEASE.
${spring-boot.formatted-version} 您使用的 Spring Boot 版本格式化之后显示(用括号括起来,以 v 为前缀)。例如 (v2.1.1.RELEASE)。
${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)或者都不打印(off)。

打印的 banner 被注册名为 springBootBanner 的单例 bean。

注意

YAML 将 off 映射为 false,因此如果要禁用应用程序 banner,请确保属性添加引号。

  1. spring:
  2. main:
  3. banner-mode: "off"

23.3、自定义 SpringApplication

如果 SpringApplication 的默认设置不符合您的想法,您可以创建本地实例进行定制化。例如,要关闭 banner,您可以这样:

  1. public static void main(String[] args) {
  2. SpringApplication app = new SpringApplication(MySpringConfiguration.class);
  3. app.setBannerMode(Banner.Mode.OFF);
  4. app.run(args);
  5. }

注意

传入 SpringApplication 的构造参数是 spring bean 的配置源。大多情况下是引用 @Configuration 类,但您也可以引用 XML 配置或者被扫描的包。

也可以使用 application.properties 文件配置 SpringApplication。有关详细信息,请参见第 24 章:外部化配置

关于配置选项的完整列表,请参阅 SpringApplication Javadoc

23.4、Fluent Builder API

如果您需要构建一个有层级关系的 ApplicationContext(具有父/子关系的多上下文),或者偏向使用 fluent(流式)构建器 API,可以使用 SpringApplicationBuilder

SpringApplicationBuilder 允许您链式调用多个方法,包括能创建出具有层次结构的 parentchild 方法。

例如:

  1. new SpringApplicationBuilder()
  2. .sources(Parent.class)
  3. .child(Application.class)
  4. .bannerMode(Banner.Mode.OFF)
  5. .run(args);

注意

创建层级的 ApplicationContext 时有部分限制,比如 Web 组件必须包含在子上下文中,并且相同的 Environment 将作用于父子上下文。有关详细信息,请参阅 SpringApplicationBuilder Javadoc

23.5、应用程序事件与监听器

除了常见的 Spring Framework 事件,比如 ContextRefreshedEventSpringApplication 还会发送其他应用程序事件。

注意

ApplicationContext 创建之前,实际上触发了一些事件,因此您不能像 @Bean 一样注册监听器。您可以通过 SpringApplication.addListeners(​...) 或者 SpringApplicationBuilder.listeners(...​) 方法注册它们。如果您希望无论应用使用何种创建方式都能自动注册这些监听器,您都可以将 META-INF/spring.factories 文件添加到项目中,并使用 org.springframework.context.ApplicationListener 属性键指向您的监听器。比如:org.springframework.context.ApplicationListener=com.example.project.MyListener

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

  1. 在开始应用开始运行但还没有进行任何处理时(除了注册监听器和初始化器[initializer]),将发送 ApplicationStartingEvent
  2. Environment 被上下文使用,但是在上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent
  3. 在开始刷新之前,bean 定义被加载之后发送 ApplicationPreparedEvent
  4. 在上下文刷新之后且所有的应用和命令行运行器(command-line runner)被调用之前发送 ApplicationStartedEvent
  5. 在应用程序和命令行运行器(command-line runner)被调用之后,将发出 ApplicationReadyEvent,该事件用于通知应用已经准备处理请求。
  6. 如果启动时发生异常,将发送 ApplicationFailedEvent

提示

您可能不会经常使用应用程序事件,但了解他们的存在还是很有必要的。在框架内部,Spring Boot 使用这些事件来处理各种任务。

应用程序事件发送使用了 Spring Framework 的事件发布机制。该部分机制确保在子上下文中发布给监听器的事件也会发布给所有祖先上下文中的监听器。因此,如果您的应用程序使用有层级结构的 SpringApplication 实例,则监听器可能会收到同种类型应用程序事件的多个实例。

为了让监听器能够区分其上下文事件和后代上下文事件,您应该注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware 来注入上下文,如果监听器是 bean,则使用 @Autowired 注入上下文。

23.6、Web 环境

SpringApplication 试图为您创建正确类型的 ApplicationContext。确定 WebApplicationType 的算法非常简单:

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

这意味着如果您在同一个应用程序中使用了 Spring MVC 和 Spring WebFlux 中的新 WebClient,默认情况下将使用 Spring MVC。您可以通过调用 setWebApplicationType(WebApplicationType) 修改默认行为。

也可以调用 setApplicationContextClass(...) 来完全控制 ApplicationContext 类型。

提示

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

23.7、访问应用程序参数

如果您需要访问从 SpringApplication.run(​...) 传入的应用程序参数,可以注入一个 org.springframework.boot.ApplicationArguments bean。ApplicationArguments 接口提供了访问原始 String[] 参数以及解析后的 optionnon-option 参数的方法:

  1. import org.springframework.boot.*;
  2. import org.springframework.beans.factory.annotation.*;
  3. import org.springframework.stereotype.*;
  4. @Component
  5. public class MyBean {
  6. @Autowired
  7. public MyBean(ApplicationArguments args) {
  8. boolean debug = args.containsOption("debug");
  9. List<String> files = args.getNonOptionArgs();
  10. // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
  11. }
  12. }

提示

Spring Boot 还向 Spring Environment 注册了一个 CommandLinePropertySource。这允许您可以使用 @Value 注解注入单个应用参数。

23.8、使用 ApplicationRunner 或 CommandLineRunner

如果您需要在 SpringApplication 启动时运行一些代码,可以实现 ApplicationRunner 或者 CommandLineRunner 接口。这两个接口的工作方式是一样的,都提供了一个单独的 run 方法,它将在 SpringApplication.run(​...) 完成之前调用。

CommandLineRunner 接口提供了访问应用程序字符串数组形式参数的方法,而 ApplicationRunner 则使用了上述的 ApplicationArguments 接口。以下示例展示 CommandLineRunnerrun 方法的使用:

  1. import org.springframework.boot.*;
  2. import org.springframework.stereotype.*;
  3. @Component
  4. public class MyBean implements CommandLineRunner {
  5. public void run(String... args) {
  6. // Do something...
  7. }
  8. }

如果您定义了多个 CommandLineRunner 或者 ApplicationRunner bean,则必须指定调用顺序,您可以实现 org.springframework.core.Ordered 接口,也可以使用 org.springframework.core.annotation.Order 注解解决顺序问题。

23.9、应用程序退出

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

此外,如果希望在调用 SpringApplication.exit() 时返回特定的退出码,则 bean 可以实现 org.springframework.boot.ExitCodeGenerator 接口。之后退出码将传递给 System.exit() 以将其作为状态码返回,如示例所示:

  1. @SpringBootApplication
  2. public class ExitCodeApplication {
  3. @Bean
  4. public ExitCodeGenerator exitCodeGenerator() {
  5. return () -> 42;
  6. }
  7. public static void main(String[] args) {
  8. System.exit(SpringApplication
  9. .exit(SpringApplication.run(ExitCodeApplication.class, args)));
  10. }
  11. }

此外,ExitCodeGenerator 接口可以通过异常实现。遇到这类异常时,Spring Boot 将返回实现的 getExitCode() 方法提供的退出码。

23.10、管理功能

可以通过指定 spring.application.admin.enabled 属性来为应用程序启用管理相关的功能。其将在 MBeanServer 平台上暴露 SpringApplicationAdminMXBean。您可以使用此功能来远程管理 Spring Boot 应用。该功能对服务包装器的实现也是非常有用的。

提示

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

注意

启用此功能时请小心,因为 MBean 暴露了关闭应用程序的方法。