这一节会深入Spring Boot的细节。我们将带你了解几个重要特性,这些是你可能会用到和自定义的。如果你没有了解过Spring Boot,可以从getting-started.html,using-spring-boot.html开始,以便你有一个好基础。
1.Spring应用
运行main方法下的SpringApplication类为我们提供了一种便利途径去引导一个Spring应用。多数情况下,都是委托静态的SpringApplication.run方法,如下:
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}
应用启动成功后,应该能看到如下的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: v2.2.1.RELEASE
2019-04-31 13:09:54.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)
2019-04-31 13:09:54.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
2019-04-01 13:09:56.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080
2019-04-01 13:09:57.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)
默认情况下,日志级别是INFO,还有一些与启动细节相关的信息,比如启动应用的user。如果想要设置更高的日志级别,请参考Log Levels。
1.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 实现,你也可以用自己的 add your own。
如果失败分析器不能处理异常,你也可以获得完整的报告信息以便知道哪儿出了问题。开启debug属性 (enable the debug
property)或者为org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener开启DEBUG日志(enable DEBUG
logging)。
如果你用java -jar命令启动应用,可以用以下方式开启debug属性。
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
1.2 懒初始化
SpringApplication可以实现懒初始化,懒初始化时所有的beans在需要的时候才会被创建,而不是应用一启动就创建。因此,懒初始化可以减少应用启动时间。在web应用中,开启懒初始化会导致与网络相关的beans直到收到HTTP请求时才进行初始化。
懒初始化的缺点是不能让我们及时发现问题。如果一个没有配置的bean是懒初始化的,启动时就不会再失败,只有当这个bean被初始化时问题才显示出来。还要小心保证JVM有足够的内存容纳所有的应用beans而不仅仅是哪些启动时被初始化的beans。由于以上原因,懒初始化默认关闭,开启懒初始化之前建议调整一下JVM堆内存大小。
懒初始化可以在程序中开启,使用SpringApplicationBuilder 的lazyInitialization 方法、SpringApplication的setLazyInitialization 方法,也可以在配置中开启:
spring.main.lazy-initialization=true
可以使用@Lazy(false)注解单独为类设置懒初始化属性。
1.3 自定义Banner
应用启动时打印的banner可以自定义,在classpath下增加一个banner.txt文件或者把这个文件的路径设置为spring.banner.location属性的值。如果文件的编码不是UTF-8,也可以设置spring.banner.charset。除了文本文件,也可以在classpath下增加banner.gif, banner.jpg, 或者 banner.png这些图片文件,或者设置spring.banner.image.location属性。图片会被转换成ASCII 的艺术表现形式并打印在文本banner上。
在banner.txt文件中,可以使用以下的占位符(placeholders),点击链接查看:https://www.cnblogs.com/mihasha/p/11882617.html
SpringApplication.setBanner(…)可以让我们在程序中生成banner。使用org.springframework.boot.Banner接口实现自己的printBanner()方法。
你也可以使用spring.main.banner-mode属性来决定Banner是打印到控制台上,发到日志中或者干脆不要banner。打印的banner是一个单例的bean,名为SpringBootBanner。
1.4 自定义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文件进行配置。查看 Externalized Configuration
了解更多细节。
配置项的完整列表查看这里 SpringApplication
Javadoc
1.5 流畅的Builder API
如果你想构建一个ApplicationContext 层级(具有父子关系的多context)或者更喜欢使用“流畅的”builder API。你可以使用SpringApplicationBuilder。
SpringApplicationBuilder支持方法调用链,包括有父子关系的方法,如下所示。
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
ApplicationContext层级关系有很多限制。例如,web组件必须在子context中,父子context必须是同样的环境。点击
SpringApplicationBuilder
Javadoc
了解完整细节。
1.6 应用事件和监听器。
除了类似ContextRefreshedEvent
这样平常的Spring框架事件外,SpringApplication 会send一些额外的应用事件。
有些事件实际上在ApplicationContext 创建之前就被触发了,所以你无法在那些@Bean注解的类上注册监听器。SpringApplication.addListeners(…) 或者 SpringApplicationBuilder.listeners(…)方法可以用来注册监听器。
随着应用的启动,事件顺序如下:
1.ApplicationStartingEvent : 除了监听器和初始化器之外,开始运行后,进行其他处理之前;
2.ApplicationEnvironmentPreparedEvent :明确context的环境时,context创建之前;
3.ApplicationContextInitializedEvent : ApplicationContext 准备完毕,ApplicationContextInitializers 调用之后,定义的bean加载之前;
4.ApplicationPreparedEvent : bean加载后,refresh开始之前;
5.ApplicationStartedEvent : after the context has been refreshed but before any application and command-line runners have been called.
6.ApplicationReadyEvent :after any application and command-line runners have been called. It indicates that the application is ready to service requests.
7.ApplicationFailedEvent : 启动异常时。
以上列表仅包含与SpringApplication绑定的SpringApplicationEvents 。除此之外,在ApplicationPreparedEvent 与ApplicationStartedEvent之间的事件:
1.ContextRefreshedEvent :ApplicationContext 刷新时;
2.WebServerInitializedEvent :WebServer 准备完毕后。ServletWebServerInitializedEvent and ReactiveWebServerInitializedEvent are the servlet and reactive variants respectively。
你可能不会用到应用事件,但是了解他们也很有用。Spring Boot内部使用事件来处理各种各样的任务。
使用Spring框架的事件发布机制发送应用事件。部分机制确保发布到子context listener的事件也会发布到任何父级context的listener。因此,如果你的应用使用了SpringApplication实例的层级关系,一个listener可能会收到多个同种类型的应用事件实例。
为了让listener能够区分来自自己context的事件和来自下级context的事件,要求应用的context要被注入,然后比较注入的context和事件的context。可以实现ApplicationContextAware 来注入context,如果listener是一个bean,可以使用@Autowired。
1.7 Web Environment
SpringApplication会努力为你创建正确的ApplicationContext类型,决定WebApplicationType 的算法相当的简单:
- 如果使用了Spring MVC,就用AnnotationConfigServletWebServerApplicationContext
- 如果没用Spring MVC而是Spring WebFlux,就用AnnotationConfigReactiveWebServerApplicationContext
- 其他情况就用AnnotationConfigApplicationContext
这就意味着在一个应用中如果你既使用了Spring MVC又使用了Spring WebFlux的WebClient,那么会默认使用Spring MVC。当然你可以调用setWebApplicationType(WebApplicationType)来覆盖默认值。
也可以通过setApplicationContextClass(…)方法使用你希望的ApplicationContext类型。
使用SpringApplication进行JUnit test时,调用setWebApplicationType(WebApplicationType.NONE)方法是可取的。
1.8 获取应用参数
如果你需要传给应用的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环境下,Spring Boot注册了一个CommandLinePropertySource。它可以让你使用@Value注解注入一个应用参数。
1.9 ApplicationRunner 或者CommandLineRunner
如果SpringApplication 启动时你要运行一段特殊的代码,你可以实现ApplicationRunner或者CommandLineRunner接口。两个接口原理相同,都只提供了一个run方法,这个方法会在SpringApplication.run(…)完成前调用。
CommandLineRunner可以获取应用的字符串数组形式的参数,ApplicationRunner使用之前提到过的ApplicationArguments 接口。下面是一个CommandLineRunner 的例子:
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注解。
1.10 结束应用
每个SpringApplication都在JVM中注册了一个shutdown钩子以确保程序结束运行时ApplicationContext 能优雅的关闭。所有的Spring生命周期回调都能使用(例如DisposableBean 接口或者@PreDestroy注解)。
此外,调用SpringApplication.exit()时,实现了org.springframework.boot.ExitCodeGenerator接口的beans可以返回一个exit code。这个退出码会传给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()方法提供的状态码。
1.11 Admin Features
通过设置spring.application.admin.enabled属性,可以开启管理相关的功能。这样会在MBeanServer平台上暴露SpringApplicationAdminMXBean
。你可以使用这个功能远程管理Spring Boot应用。This feature could also be useful for any service wrapper implementation(服务包装器?).
如果你想知道应用运行在哪个HTTP端口上,通过local.server.port键来获取。