23. SpringApplication

SpringApplication类提供了一种方便的方式来引导将从main()方法启动的 Spring 应用程序。在许多情况下,您可以只委托给静态SpringApplication.run方法:

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

当您的应用程序启动时,您应该看到类似于以下内容:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::   v1.5.9.RELEASE

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)
2013-07-31 00:08:16.166  INFO 56603 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot[emailprotected]6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy
2014-03-04 13:09:54.912  INFO 41370 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
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条记录消息,包括一些相关的启动详细信息,例如启动该应用程序的用户。

23.1 启动失败

如果您的应用程序无法启动,则已注册的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's listening on port 8080 or configure this application to listen on another port.

Note

Spring Boot 提供了众多的FailureAnalyzer实现,您可以很轻松地添加自己的

如果没有故障分析器能够处理异常,您仍然可以显示完整的自动配置报告,以更好地了解出了什么问题。为此,您需要org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer 启用调试属性启用调试日志记录

例如,如果您使用java -jar运行应用程序,则可以启用debug属性,如下所示:

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

23.2 自定义 banner

可以通过将banner.txt文件添加到您的 Classpath 或将banner.location设置为此类文件的位置来更改启动时打印的 banner。如果文件编码异常,则可以设置banner.charset(默认为UTF-8)。除了文本文件,您还可以将banner.gifbanner.jpgbanner.png图像文件添加到 Classpath,或设置banner.image.location属性。图像将转换为 ASCII 艺术作品并打印在任何 Literalsbanner 上方。

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

表 23.1 标语变量

VariableDescription
${application.version}MANIFEST.MF中声明的应用程序的版本号。例如Implementation-Version: 1.0被打印为1.0
${application.formatted-version}MANIFEST.MF声明的格式显示的应用程序版本号(用括号括起来并以v开头)。例如(v1.0)
${spring-boot.version}您正在使用的 Spring Boot 版本。例如1.5.9.RELEASE
${spring-boot.formatted-version}您正在使用的 Spring Boot 版本经过格式化以用于显示(用方括号括起来,并以v作为前缀)。例如(v1.5.9.RELEASE)
${Ansi.NAME}(或${AnsiColor.NAME}${AnsiBackground.NAME}${AnsiStyle.NAME})其中NAME是 ANSI 转义代码的名称。有关详情,请参见AnsiPropertySource
${application.title}MANIFEST.MF中声明的应用程序标题。例如Implementation-Title: MyApp被打印为MyApp

Tip

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

您还可以使用spring.main.banner-mode属性来确定是否必须使用配置的 Logger(log)将 banner 打印在System.out(console)上或根本不打印(off)。

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

Note

YAML 将offMap 到false,因此,如果要在应用程序中禁用 banner,请确保添加引号。

spring:
main:
banner-mode: "off"

23.3 自定义 SpringApplication

如果您不喜欢SpringApplication默认值,则可以创建一个本地实例并对其进行自定义。例如,要关闭 banner,您可以编写:

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

Note

传递给SpringApplication的构造函数参数是 spring bean 的配置源。在大多数情况下,它们将是对@Configuration类的引用,但它们也可能是对 XML 配置或应扫描的程序包的引用。

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

有关配置选项的完整列表,请参见SpringApplication Javadoc

23.4 Fluent Builder API

如果您需要构建ApplicationContext层次结构(具有父/子关系的多个上下文),或者只想使用“Fluent 的”构建器 API,则可以使用SpringApplicationBuilder

SpringApplicationBuilder允许您将多个方法调用链接在一起,并包括parentchild方法,这些方法允许您创建层次结构。

For example:

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

Note

构建ApplicationContext阶层时有一些限制,例如 Web 组件 必须 包含在子上下文中,并且相同的Environment将用于父上下文和子上下文。有关详细信息,请参见SpringApplicationBuilder Javadoc

23.5 应用程序事件和侦听器

除了通常的 Spring Framework 事件(例如ContextRefreshedEvent)之外,SpringApplication还发送一些其他应用程序事件。

Note

有些事件实际上是在创建ApplicationContext之前触发的,因此您不能将这些事件注册为@Bean。您可以通过SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)方法注册它们。

如果您希望不管应用程序的创建方式如何自动注册那些侦听器,则可以将META-INF/spring.factories文件添加到项目中,并使用org.springframework.context.ApplicationListener键引用您的侦听器。

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

应用程序事件在您的应用程序运行时按以下 Sequences 发送:

  • ApplicationStartingEvent在运行开始时发送,但在进行任何处理(注册侦听器和初始化程序除外)之前都将发送。

  • 当知道要在上下文中使用的Environment时,但在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent

  • 刚开始刷新之前但在加载 bean 定义之后发送ApplicationPreparedEvent

  • 刷新后发送ApplicationReadyEvent,并且已处理所有相关的回调,以指示应用程序已准备好处理请求。

  • 如果启动时出现异常,则会发送ApplicationFailedEvent

Tip

您通常不需要使用应用程序事件,但是知道它们的存在会很方便。在内部,Spring Boot 使用事件来处理各种任务。

应用程序事件是使用 Spring Framework 的事件发布机制发送的。该机制的一部分确保了在子上下文中发布给侦听器的事件也将在任何祖先上下文中发布给侦听器。结果,如果您的应用程序使用SpringApplication实例的层次结构,则侦听器可能会收到同一类型的应用程序事件的多个实例。

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

23.6 网络环境

SpringApplication会代表您尝试创建正确的ApplicationContext类型。默认情况下,将使用AnnotationConfigApplicationContextAnnotationConfigEmbeddedWebApplicationContext,具体取决于您是否在开发 Web 应用程序。

用于确定“网络环境”的算法非常简单(基于几个类的存在)。如果您需要覆盖默认值,则可以使用setWebEnvironment(boolean webEnvironment)

也可以完全控制将通过调用setApplicationContextClass(…)使用的ApplicationContext类型。

Tip

在 JUnit 测试中使用SpringApplication时,通常希望调用setWebEnvironment(false)

23.7 访问应用程序参数

如果您需要访问传递给SpringApplication.run(…)的应用程序参数,则可以注入org.springframework.boot.ApplicationArguments bean。 ApplicationArguments接口提供对原始String[]参数以及已解析的optionnon-option参数的访问:

import org.springframework.boot.*
import org.springframework.beans.factory.annotation.*
import org.springframework.stereotype.*

@Component
public class MyBean {

    @Autowired
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }

}

Tip

Spring Boot 还将在 Spring Environment中注册一个CommandLinePropertySource。这还允许您使用@ValueComments 注入单个应用程序参数。

23.8 使用 ApplicationRunner 或 CommandLineRunner

如果在SpringApplication启动后需要运行一些特定的代码,则可以实现ApplicationRunnerCommandLineRunner接口。两个接口以相同的方式工作,并提供一个run方法,该方法将在SpringApplication.run(…)完成之前被调用。

CommandLineRunner接口提供对应用程序参数的访问,作为简单的字符串数组,而ApplicationRunner接口则使用上面讨论的ApplicationArguments接口。

import org.springframework.boot.*
import org.springframework.stereotype.*

@Component
public class MyBean implements CommandLineRunner {

    public void run(String... args) {
        // Do something...
    }

}

如果定义了几个必须按特定 Sequences 调用的CommandLineRunnerApplicationRunner bean,则可以另外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。

23.9 申请退出

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

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

@SpringBootApplication
public class ExitCodeApplication {

	@Bean
	public ExitCodeGenerator exitCodeGenerator() {
		return new ExitCodeGenerator() {
			@Override
			public int getExitCode() {
				return 42;
			}
		};
	}

	public static void main(String[] args) {
		System.exit(SpringApplication
				.exit(SpringApplication.run(ExitCodeApplication.class, args)));
	}

}

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

23.10Management 员功能

通过指定spring.application.admin.enabled属性,可以为应用程序启用与 Management 员相关的功能。这将在平台MBeanServer上公开SpringApplicationAdminMXBean。您可以使用此功能来远程 ManagementSpring Boot 应用程序。这对于任何服务包装器实现也可能有用。

Tip

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

Note

启用此功能时要小心,因为 MBean 公开了一种关闭应用程序的方法。