23、SpringApplication
SpringApplication
类提供了一种可通过运行 main()
方法来启动 Spring 应用的简单方式。多数情况下,您只需要委托给静态的 SpringApplication.run
方法:
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}
当应用启动时,您应该会看到类似以下的内容输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: v2.1.1.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] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@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.TomcatServletWebServerFactory : 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
级别的日志信息,包括一些应用启动相关信息。如果您需要修改 INFO
日志级别,请参考 26.4 部分:日志等级。
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.
注意
Spring Boot 提供了许多的
FailureAnalyzer
实现,您也可以添加自己的实现。
如果没有失败分析器能够处理的异常,您仍然可以显示完整的条件报告以便更好地了解出现的问题。为此,您需要针对 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
启用 debug
属性或者开启 DEBUG
日志。
例如,如果您使用 java -jar
运行应用,可以按以下方式启用 debug
属性:
$ 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.gif
、banner.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.out
(console
)上打印 banner,还是使用日志记录器(log
)或者都不打印(off
)。
打印的 banner 被注册名为 springBootBanner
的单例 bean。
注意
YAML 将
off
映射为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);
}
注意
传入
SpringApplication
的构造参数是 spring bean 的配置源。大多情况下是引用@Configuration
类,但您也可以引用 XML 配置或者被扫描的包。
也可以使用 application.properties
文件配置 SpringApplication
。有关详细信息,请参见第 24 章:外部化配置。
关于配置选项的完整列表,请参阅 SpringApplication Javadoc。
23.4、Fluent Builder API
如果您需要构建一个有层级关系的 ApplicationContext
(具有父/子关系的多上下文),或者偏向使用 fluent(流式)构建器 API,可以使用 SpringApplicationBuilder
。
SpringApplicationBuilder
允许您链式调用多个方法,包括能创建出具有层次结构的 parent
和 child
方法。
例如:
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
注意
创建层级的
ApplicationContext
时有部分限制,比如 Web 组件必须包含在子上下文中,并且相同的Environment
将作用于父子上下文。有关详细信息,请参阅 SpringApplicationBuilder Javadoc。
23.5、应用程序事件与监听器
除了常见的 Spring Framework 事件,比如 ContextRefreshedEvent
,SpringApplication
还会发送其他应用程序事件。
注意
在
ApplicationContext
创建之前,实际上触发了一些事件,因此您不能像@Bean
一样注册监听器。您可以通过SpringApplication.addListeners(...)
或者SpringApplicationBuilder.listeners(...)
方法注册它们。如果您希望无论应用使用何种创建方式都能自动注册这些监听器,您都可以将META-INF/spring.factories
文件添加到项目中,并使用org.springframework.context.ApplicationListener
属性键指向您的监听器。比如:org.springframework.context.ApplicationListener=com.example.project.MyListener
当您运行应用时,应用程序事件将按照以下顺序发送:
- 在开始应用开始运行但还没有进行任何处理时(除了注册监听器和初始化器[initializer]),将发送
ApplicationStartingEvent
。 - 当
Environment
被上下文使用,但是在上下文创建之前,将发送ApplicationEnvironmentPreparedEvent
。 - 在开始刷新之前,bean 定义被加载之后发送
ApplicationPreparedEvent
。 - 在上下文刷新之后且所有的应用和命令行运行器(command-line runner)被调用之前发送
ApplicationStartedEvent
。 - 在应用程序和命令行运行器(command-line runner)被调用之后,将发出
ApplicationReadyEvent
,该事件用于通知应用已经准备处理请求。 - 如果启动时发生异常,将发送
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[]
参数以及解析后的 option
和 non-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"]
}
}
提示
Spring Boot 还向 Spring
Environment
注册了一个CommandLinePropertySource
。这允许您可以使用@Value
注解注入单个应用参数。
23.8、使用 ApplicationRunner 或 CommandLineRunner
如果您需要在 SpringApplication 启动时运行一些代码,可以实现 ApplicationRunner
或者 CommandLineRunner
接口。这两个接口的工作方式是一样的,都提供了一个单独的 run
方法,它将在 SpringApplication.run(...)
完成之前调用。
CommandLineRunner
接口提供了访问应用程序字符串数组形式参数的方法,而 ApplicationRunner
则使用了上述的 ApplicationArguments
接口。以下示例展示 CommandLineRunner
和 run
方法的使用:
import org.springframework.boot.*;
import org.springframework.stereotype.*;
@Component
public class MyBean implements CommandLineRunner {
public void run(String... args) {
// Do something...
}
}
如果您定义了多个 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()
以将其作为状态码返回,如示例所示:
@SpringBootApplication
public class ExitCodeApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication
.exit(SpringApplication.run(ExitCodeApplication.class, args)));
}
}
此外,ExitCodeGenerator
接口可以通过异常实现。遇到这类异常时,Spring Boot 将返回实现的 getExitCode()
方法提供的退出码。
23.10、管理功能
可以通过指定 spring.application.admin.enabled
属性来为应用程序启用管理相关的功能。其将在 MBeanServer
平台上暴露 SpringApplicationAdminMXBean
。您可以使用此功能来远程管理 Spring Boot 应用。该功能对服务包装器的实现也是非常有用的。
提示
如果您想知道应用程序在哪一个 HTTP 端口上运行,请使用
local.server.port
键获取该属性。
注意
启用此功能时请小心,因为 MBean 暴露了关闭应用程序的方法。