文章作者:Tyan
博客:noahsnail.com  |  CSDN  |  简书
Part IV. Spring Boot特性
这一部分进入Spring Boot细节部分。在这部分你会了解到你想使用和定制的一些重要特性。如果你还没准备好,你可以阅读第二部分“Getting started”和第三部分“Using Spring Boot”,可以对基础知识有个较好的认识。
23. SpringApplication
SpringApplication提供了一种很方便的方式来引导Spring应用,Spring应用可以从main()方法中启动。许多情况下你可以委托给静态方法SpringApplication.run:
public static void main(String[] args) {
    SpringApplication.run(MySpringConfiguration.class, args);
}
当你的应用启动时你应该看到类似于下面的东西:
 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::   v1.4.2.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.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@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.
Spring Boot提供了许多
FailureAnalyzer实现,你可以很容易添加自己的FailureAnalyzer实现。
如果没有失败分析器能处理这个异常,你仍可以显示完整的自动配置报告,从而更好的理解什么地方出问题了。为了实现这个你需要启用debug属性或启用org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer的DEBUG日志。
例如,如果你使用java -jar运行应用,你可以用下面的形式启用debug属性:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
23.2 定制Banner
启动时打印的标语可以通过在classpath中添加一个banner.txt文件或者将banner.location设置为banner.txt文件的位置来修改。如果文件是一种不常见的编码方式,你可以设置banner.charset(默认是UTF-8)。除了文本文件之外,你也添加一个banner.gif,banner.jpg或banner.png图像文件到classpath中,或者设置一个banner.image.location属性。图像将被转换成ASCII艺术表示并打印在文本标语之上。
在banner.txt内部你可以使用下面的任何占位符:
Table 23.1. Banner变量
| 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版本。例如1.4.2.RELEASE. | 
| ${spring-boot.formatted-version} | 你使用的Spring Boot版本进行格式化显示加上前缀v并用括号包裹)。例如(v1.4.2.RELEASE)。 | 
| ${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}) | 
NAME是ANSI转义码的名字。更多细节请看AnsiPropertySource。 | 
| ${application.title} | 在MANIFEST.MF中声明的应用标题。例如Implementation-Title: MyApp打印成MyApp. | 
如果你想自动生成一个标语你可以使用
SpringApplication.setBanner(…)方法。使用org.springframework.boot.Banner接口并实现你自己的printBanner()方法。
你也可以使用spring.main.banner-mode属性来决定标语是否必须在System.out(控制台)上输出,使用配置的日志(log)或一点也不用(off)。
如果你想在你的应用中禁用banner,YAML会将
off映射为false,因此要确保添加引用。
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 beans配置源。在大多数情况下将会引用@Configuration类,但它们也可以引用XML配置或应该扫描的包。
也可以使用application.properties文件配置SpringApplication。更多细节请看24章,『外部配置』。
完整的配置选项列表,请看SpringApplication文档。
23.4 流畅的构建器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文档。
23.5 应用事件和监听器
除了平常的Spring框架事件之外,例如ContextRefreshedEvent,SpringApplication会发送一些其它的应用事件。
在
ApplicationContext创建之前实际上会触发一些事件,因此你不能使用@Bean来注册这些监听器。你可以通过SpringApplication.addListeners(…)或SpringApplicationBuilder.listeners(…)方法来注册这些监听器。
如果你想自动注册这些监听器,不管上下文的创建方式,你可以在你的工程中添加
META-INF/spring.factories文件,并通过org.springframework.context.ApplicationListener作为key来引用你的监听器。
org.springframework.context.ApplicationListener=com.example.project.MyListener
当你的应用运行时,应用事件以下面的顺序发送:
- 在运行启动时发送
ApplicationStartedEvent,除了监听器和初始化器注册之外,在进行任何处理之前发送。 - 当在上下文中使用的
Environment已知时,发送ApplicationEnvironmentPreparedEvent,但发送是在上下文创建之前。 - 在再刷新启动之前,但在bean定义加载之后,发送
ApplicationPreparedEvent。 - 在再刷新之后,发送
ApplicationReadyEvent,任何相关的回调函数都处理完成之后,意味着应用已经准备处理服务请求了。 - 如果启动时出现异常,发送
ApplicationFailedEvent. 
经常你不需要使用应用事件,但知道它们的存在是便利的。Spring Boot内部使用事件来处理大量的任务。
23.6 Web环境
SpringApplication会尝试创建代表你的合适的ApplicationContext类型。默认情况下,会使用AnnotationConfigApplicationContext或AnnotationConfigEmbeddedWebApplicationContext,依赖于你是否在开发一个web应用。
使用的决定web environment的算法是相对简单的(基于现有的一些类)。如果你需要覆写默认值你可以使用setWebEnvironment(boolean webEnvironment)。
完全控制ApplicationContext类型也是可能的,通过调用setApplicationContextClass(…)使用。
当在JUnit测试时使用
SpringApplication,经常需要调用setWebEnvironment(false)。
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(…)之前会调用这个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...
    }
}
另外,如果定义的CommandLineRunner或ApplicationRunner beans必须以指定顺序调用,你可以实现org.springframework.core.Ordered接口或org.springframework.core.annotation.Order 注解。
23.9 应用退出
为了确保ApplicationContext在关闭时安全退出, 每个SpringApplication都会在JVM中注册一个关闭钩子。所有的标准Spring生命周期回调函数(例如DisposableBean接口,或@PreDestroy注解)都会被使用。
另外,当应用退出时,如果它们想返回一个特定的退出码,beans可以实现org.springframework.boot.ExitCodeGenerator接口。
23.10 Admin功能
如果应用想启用admin相关的功能,可以指定spring.application.admin.enabled属性。这会在平台MBeanServer上暴露SpringApplicationAdminMXBean。你可以使用这个功能远程的管理你的Spring Boot应用。对于任何服务包裹的实现这是很有用的。
如果你想知道应用运行的HTTP接口,通过关键字
local.server.port可以得到这个属性。
当启用这个功能时要非常小心,因为MBean会暴露一个关闭应用的方法。