SpringBoot Features

.1. Application Availability

系统可用性

可以通过注入 ApplicationAvailability 接口到 bean 中以获取应用可用性状态。

.1.1. Liveness State

应用活性,用以表明应用内部状态是否能正常工作,或是否能从异常中自动恢复。如果不能(失活),基础平台应该重启应用。一般来说,应用活性不应该基于外部检查,否则外部检查系统(数据库、缓存、Web API)出现异常,将触发大量重启与平台累积的失败。

Spring Boot 应用的内部状态一般取决于 ApplicationContext。如果 ApplicationContext 成功启动,Spring Boot 就将认为应用在有效的状态。只要 context be refreshed ,应用就被认为是有活性的。

.1.2. Readiness State

应用就绪状态,用以表明应用是否对处理流量准备就绪。未进入就绪状态将告诉 platform 平台此时不宜路由流量到应用。典型的场景是在应用启动阶段,当 CommandLineRunnerApplicationRunner 组件正在被处理时,或者应用忙于其他流量请求时而不能处理当前流量请求时。

需要在程序启动阶段添加任务可以通过将 bean 实现接口 ApplicationRunnerCommandLineRunner,而不是使用 Spring 组件生命周期回调 @PostConstruct。以上两个接口分别可获取到程序启动参数和命令行参数。

.1.3. Managing the Application Availability State

  1. 获取应用当前可用性状态:注入 ApplicationAvailability 接口并调用其方法;
  2. 监听应用可用性变化:
1
2
3
4
5
6
7
8
9
10
11
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC:
// create file /tmp/healthy
break;
case REFUSING_TRAFFIC:
// remove file /tmp/healthy
break;
}
}

更新应用可用性状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final ApplicationEventPublisher eventPublisher;
//自动 constructor 注入 publisher
public LocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}

public void checkLocalCache() {
try {
//...
}
catch (CacheCompletelyBrokenException ex) {
//发布一个可用性更新事件
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
}
}

.2. Application Events and Listeners

应用事件与监听器

除了常规的应用事件 ContextRefreshedEvent, 一个 Spring 应用还可以发送其他事件。其中一些事件在整个应用上下文 ApplicationContext 创建前,对于这种事件并不能注册其监听器为一个 Bean ,但可以通过 SpringApplication.addListeners()SpringApplicationBuilder.listeners() 方法注册。如果需要不考虑上下文是否创建自动注册监听器,可以在工程中 META-INF/spring.factories 引入监听器,使用 key org.springframework.context.ApplicationListener=com.myproject.MyListener

应用开始启动后,事件发布顺序如下:

  1. ApplicationStartingEvent 在应用刚开始启动,除 listeners initializers 已注册外,所有的其他处理工作都还未开启时。
  2. ApplicationEnvironmentPreparedEvent 事件发布时机:Environment 在上下文中已清晰但上下文还未创建时
  3. ApplicationContextInitializedEventApplicationContext 已准备且 ApplicationInitializers 已被调用但 bean definitions 还未加载时
  4. ApplicationPreparedEvent refreshed 开始之前 bean definitions 加载之后
  5. ApplicationStartedEventcontext refreshed 之后,application runners 与 command-line runners 调用之前
  6. AvailabilityChangeEventLivenessState.CORRECT 可用性表明应用程序已具活性后
  7. ApplicationReadyEvent 在所有的 application/command-line runner 被调用之后
  8. AvailabilityChangeEvent 应用已为服务请求准备就绪 ReadinessState.ACCEPTING_TRAFFIC 之后
  9. ApplicationFailedEvent 启动异常之后

以上 9 个事件是与 SpringApplication 绑定的事件,除此外,在 ApplicationPreparedEventApplicationStartedEvent 之间还有两个事件:

  1. WebServerInitializedEvent WebServer 准备就绪后。而 ServletWebServerInitializedEventReactiveWebServerInitializedEvent 分别在 servlet 与 reactive variants 就绪后。
  2. ContextRefreshedEvent ApplicationContext 刷新后 when an ApplicationContext is refreshed.

Spring Boot 就是使用以上各个事件来处理各类任务。

根据以上顺序,倒推 Spring 应用启动的处理有:

  1. 注册 listeners 与 initializers
  2. 创建环境配置 Environment
  3. 创建上下文 ApplicationContext ,调用 ApplicationInitializers
  4. 加载 bean definitions
  5. 如果是 web 工程应用,加载相关 WebServer: ServletWebServer/ReactiveWebServer
  6. 刷新 ApplicationContext
  7. 改变应用可用性为活性 LivenessState.CORRECT
  8. 调用 application runner command-line runner
  9. 改变应用服务请求就绪状态为就绪 ReadinessState.ACCEPTING_TRAFFIC

Spring Framework 的事件发布机制在子 context 发布事件后,父 context 同样会收到相应的事件发布,所以如果应用使用了 SpringApplication 层级,一个监听器会收到相同类型的 application event。为了区别来自哪里,可以将 Context 注入对比。如果 listener 是个 bean 直接使用 @AutoWired 注入,如果 listener 不是 bean 需要实现 ApplicationContextAware 接口注入。

.3. Web Environment

SpringApplication 根据代码行为来创建正确的 ApplicationContext ,决定 WebApplicationType 的算法是:

  1. 如果存在 Spring MVC ,使用 AnnotationConfigServletWebApplicationContext
  2. 如果不使用 Spring MVC 使用 Spring WebFlux,则使用 AnnotationConfigReactiveWebServerApplicationContext
  3. 否则,使用 AnnotationConfigApplicationContext

算法决定了,如果一个应用使用了 Spring MVC 也使用了 Spring WebFlux ,将按 Spring MVC 处理。可使用 setWebApplicationType(WebApplicationType) 方法直接覆盖算法决定。如果要完全控制 ApplicationContext 使用方法 setApplicationContestClass(...)。在使用 JUnit 测试时,不需要 web 层,可调方法 sebWebApplicationType(WebApplicationType.NONE)

.4. Accessing Application Arguments

访问应用参数

在 Spring Boot 中,当需要访问应用参数时,注入一个 ApplicationArguments bean,即可直接访问 String[] 参数或编译成 option 与 not-option 形式的参数。

1
2
3
4
5
6
@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 也注册了一个与 Environment 关联的 CommandLinePropertySource,这就意味着可以在程序中使用 @Value 注解直接注入各个命令行参数。

.5. Using the ApplicationRunner and CommandLineRunner

使用应用、命令行Runner

如果需要在 SpringApplication 启动后执行某些代码,可以实现 ApplicationRunnerCommandLineRunner 接口,这两个接口都提供一个 run(ApplicationArguments) 方法定义,其实现都是在 SpringApplication.run(...) 方法完成之前调用。如果有多个 Runner 需要执行,可以在 Runner 上添加 Order 接口或 @Order 注解用以指定顺序。

.6. 初始化流程

spring boot 在初始化中把大量工作做了。

  1. 加载 environment 数据
  2. 创建 context
  3. context refresh
    1. 其中对入口类进行数据扫描,工作在 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass,大量调用 ConfigurationClassParser 进行数据解析。
    2. org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 该解析器方法处理所有的 configuration 配置类数据,包括各注解 @Component @ComponentScan @PropertiesSource @Import @ImportSource @Bean 内嵌类、父类、接口默认方法 数据
    3. org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>) 调用 beanFactoryPostProcessor ,其就包括了将所有的 component 中的 beanDefinition 注册到容器 BeanDefinitionRegistry 中,也包括调用所有的 BeanPostProcessor .
    4. 注册 BeanFactoryProcessor 时从 BeanDefinitionRegistry 获取其 BeanDefinition 进行初始化 bean 。
    5. org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.CglibSubclassCreator#createEnhancedSubclass 使用 CGLIB 动态代理生成 bean 子类