本节将深入Spring Boot的细节,可以学到你想使用的或定制的Spring Boot的主要特性。
1. SpringApplication
SpringApplication类为引导一个Spring应用提供了方便的方法,该Spring应用从main方法开启。通常,你可以通过静态方法SpringApplication.run方法,如下所示:
public static void main(String[] args) { SpringApplication.run(MySpringConfiguration.class, args); }
当应用启动时,你可以看到如下类似的输出,默认情况下,INFO级别的日志信息将会展示,包含相关的启动细节,如启动该应用的用户等:
. ____ _ __ _ _ /\\ / ___‘_ __ _ _(_)_ __ __ _ \ \ \ ( ( )\___ | ‘_ | ‘_| | ‘_ \/ _` | \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ‘ |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: v2.0.0.BUILD-SNAPSHOT 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.ser[email protected]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)
(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.
当任何错误分析器均无法处理该异常时,你仍然可以显示全部鉴定报告,以便更好的理解哪一块的错误。如果这样做的话,需要为"org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"启用debug属性或DEBUG日志级别。例如,如果使用java -jar运行程序时,可以按如下命令启用debug属性:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
(2) 自定义横幅
启动程序时打印的横幅如"Spring"可以进行修改,需要通过增加banner.txt文件到classpath或者设置banner.location属性指定该文件的位置。如果该文件非UTF-8编码,则你需要设置banner.charset。除了文本文件,你也可以增加banner.gif, banner.jpg或banner.png图片到classpath中,或者设置banner.image.location属性。图片将会被转换为ASCII码形式进行展现,并且展现在任何的文本横幅之上。
在banner.txt文件中,你可以使用如下的占位符:
变量 | 含义 |
${application.version} | 应用的版本号,如MANIFEST.MF所声明的。例如:Implemention-Version: 1.0 将会显示1.0 |
${application.formatted-version} | 与${application.version}类似,但会被以v开头进行格式化显示,如:v1.0 |
${spring-boot.version} | Spring Boot的版本,如2.0.0.BUILD-SNAPSHOT。 |
${spring-boot.formatted-version} | 以v开头格式化显示Spring Boot的版本,如v2.0.0.BUILD-SNAPSHOT |
${Ansi.NAME}, ${AnsiColor.NAME} ${AnsiBackground.NAME}, ${AnsiStyle.NAME} |
该四个关系等价,NAME表示ANSI转移码的名称 |
${application.title} | 应用的名称,如MANIFEST.MF中所示,如:Implementation-Title: MyApp将打印MyApp |
注意:也可以通过调用SpringApplication.setBanner(Banner banner)方法设置横幅信息,其中的banner可通过调用org.springframework.boot.Banner接口并实现printBanner()方法进行实现。
你也可使用spring.main.banner-mode属性来决定横幅是打印在标准输出窗口(console), 输出到配置的日志log中(log),亦或是禁止生成(off)。
打印的banner将注册为单例bean对象,并且在springBootBanner名称之下。
(3) 自定义SpringApplication
如果默认的SpringApplication类不适合你的口味,你可以创建局部实例并定制该实例加以替代。例如,如下的关闭banner:
public static void main(String[] args) { SpringApplication app = new SpringApplication(MySpringConfiguration.class); app.setBannerMode(Banner.Mode.OFF); app.run(args); }
你也可以通过application.properties文件对SpringApplication进行配置。
(4) 流畅的Builder API
如果你需要构建一个ApplicationContext层级,或者你更倾向于使用流畅的构建API,你可以使用SpringApplicationBuilder. SpringApplicationBuilder可以使你连接多个方法调用,且包含parent及child方法使你创建一个层级关系,如下所示:
new SpringApplicationBuilder() .sources(Parent.class) .child(Application.class) .bannerMode(Banner.Mode.OFF) .run(args);
注意:当创建ApplicationContext层级时,有一些限制。例如,Web组件必须包含在child上下文中,相同的Environment均用于parent和child上下文中。
(5) 应用事件和监听器
除了常用的如ContextRefreshedEvent等Spring框架的事件,一个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) 一个ApplicationEnvironmentPrepareEvent将会在Environment在上下文使用时发送,但在该上下文的创建之后。
3) 一个ApplicationPrepareEvent在刷新开始前发送,但在bean定义加载之后。
4) 一个ApplicationReadyEvent在刷新及任何相关回调处理之后调用,为了通知应用已经准备好服务请求。
5) 一个ApplicationFailedEvent将会被发送,如果启动时存在异常。
注意:通常你不会使用应用事件,但知道他们的存在是有益的。本质上,Spring Boot使用事件处理很多任务。
应用事件通过Spring框架的事件发布机制进行发送。该部分机制中,确保一个事件发布到child上下文的监听器时,也会发布到parent上下文的监听器。这种情况产生的结果就是,如果你的应用使用SpringApplication实例的层级关系,一个监听器可能会接收到多个同类型的应用事件的实例。
为了允许你的监听器可以区别来自其本身上下文的事件还是来自后代上下文的事件,应该请求它的应用上下文被注入,然后比较注入的上下文和该事件的上下文。上下文的注入两种方法实现:1) 通过实现ApplicationContextAware类;2)如果监听器是一个bean,则使用@Autowired注解。
(6) Web环境
SpringApplication尝试在你的立场创建ApplicationContext的正确类型。默认情况下,AnnotationConfigApplicationContext或AnnotationConfigServletWebServerApplicationContext将会被使用,取决于你是否在开发一个web应用。
决定web环境的算法相当简单(基于当前的几个类)。如果你想覆盖默认情况,可以使用setWebEnvironment(boolean webEnvironment)。
也可以完全控制ApplicationContext的类型,通过调用setApplicationContextClass()方法。
注意:当在JUnit单元测试中使用SpringApplication时,通常要求调用setWebEnvironment(false)。
(7) 访问应用参数
如果你想访问传入SpringApplication.run()方法中的应用参数时,你可以注入一个org.soringframework.boot.ApplicationArguments bean。该ApplicationArguments接口可以为原生的string[]参数,以及解析的option和non-option参数提供访问。
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.stereotype.Component; @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环境中注册了CommandLinePropertySource。这使得你可以通过使用@Value注解来注入单个应用程序参数。
(8) 使用ApplicationRunner或CommandLineRunner
如果你想在SpringApplication开启时,运行一些特殊的代码,你可以实现ApplicationRunner或CommandLineRunner接口。两个接口工作方式相同, 均提供一个单独的run方法,该方法在SpringApplication.run()结束时将被调用。
CommandLineRunner接口提供应用程序参数string[]的访问,ApplicationRunner使用之前所说的ApplicationArguments接口。
import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class MyBean implements CommandLineRunner{ public void run(String... args) throws Exception { } }
如果定义的多个CommandLineRunner或ApplicationRunner beans必须以指定的顺序调用,可以通过如下两种方法实现:1) 实现org.springframework.core.Ordered接口 2) 使用org.springframework.core.annotation.Order注解。
(9) 程序退出
每个SpringApplicaion为JVM注册了一个关闭钩子,用于确保ApplicationContext退出时可以平和地关闭。所有标准Spring生命周期的回调均可以使用,如DisposableBean接口或@PreDestory注解。
此外,当SpringApplication.exit()调用时,如果beans想要返回特殊的退出代码,可以实现org.springframework.boot.ExitCideGenerator接口。该退出代码然后可以传入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 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()方法提供的退出代码。
(10) 管理特性
通过指定spring.application.admin.enabled属性,即可使用管理相关的特性。这将在MBeanServer平台上公开SpringApplicationAdminMXBean。你可以使用该特性来远程管理你的Spring Boot应用。这个特性将有益于任务服务的封装实现。
注意:
1) 如果你想知道当前程序运行于哪个HTTP端口,你可以通过local.server.port属性得到。
2) 小心使用该特性,因为MBean公开了关闭该应用程序的方法。