1. SpringApplication启动范例
public static void main(String[] args) {
var application = new SpringApplicationBuilder()
.lazyInitialization(true) // 懒加载
.listeners(event -> log.info("Event type {}", event)) // ApplicationListener<ApplicationEvent>
.logStartupInfo(false) // 不输出开启时的日志,例如那个Spring
.sources(WorkbenchApplication.class) // 增加更多的源(source)到应用中,这部分源会被自动注入
.web(WebApplicationType.SERVLET)
.registerShutdownHook(true) // 应用关闭hook
.build();
// application.setApplicationContextFactory(webApplicationType -> null);
application.setApplicationStartup(new BufferingApplicationStartup(20));
application.run(args);
}
2. 启动错误
错误分析器 FailureAnalyzer,用来处理spring application 上下文相关的异常,也就是启动时的异常。
配置文件位置META-INF/spring.factories
(指导Spring boot找到指定的自动配置文件),继承org.springframework.boot.diagnostics.FailureAnalyzer
。
\用来换行,否则就需要写在一行;
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.demo.analyzer.ProjectConstraintViolationFailureAnalyzer
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.FailureAnalyzer;
public class ProjectConstraintViolationFailureAnalyzer implements FailureAnalyzer {
@Override
public FailureAnalysis analyze(Throwable failure) {
return new FailureAnalysis("Failure test", "restart the service", failure);
}
}
3. 懒加载 Lazy Initialization
见代码第三行
推迟bean
加载时间,在需要时才会加载,可以降低应用启动时间。
可能的问题:
- 推迟了问题被发现的时间,因为加载时间推迟了;
使用方式:
SpringApplicationBuilder
的lazyInitialization()
方法;SpringApplication
的setLazyInitialization
方法;- 配置文件增加:
spring.main.lazy-initialization=true
注解@Lazy(false)
,可以使懒加载配置不对对应属性生效。可生效的属性包括:@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
实际使用建议斟酌一下。
4. Application事件和监听
代码第四行就是事件监听的注册代码,可以注册多个监听
除了常规的Spring框架时间,例如ContextRefreshedEvent
,一个SpringApplication
也会发送一些额外应用事件。
注:有一些时间触发比
SpringApplication
更早,可以使用@Bean
方式,通过SpringApplication.addListeners(…)
method 或者SpringApplicationBuilder.listeners(…)
method进行注册。或者,你也可用通过添加META-INF/spring.factories
文件,org.springframework.context.ApplicationListener
关键字进行注册org.springframework.context.ApplicationListener=com.example.project.MyListener
以下为应用运行时发送的事件,按发送顺序排列:
编号 | 事件 |
---|---|
1 | ApplicationStartingEvent |
2 | ApplicationEnvironmentPreparedEvent |
3 | ApplicationContextInitializedEvent |
4 | ApplicationPreparedEvent |
5 | ApplicationStartedEvent |
6 | AvailabilityChangeEvent |
7 | ApplicationReadyEvent |
8 | ApplicationReadyEvent |
9 | ApplicationFailedEvent |
不要在监听里写太多的代码,使用
ApplicationRunner
或者CommandLineRunner
如果你的应用继承了SpringApplication
实例,一个监听可能会接收到同一个事件的多个实例,可以考虑注入对应的上下文进行比较。
上下文(context)可以通过实现ApplicationContextAware
注入,或者在类型为bean
的监听中使用@Autowired
注入。
出现该问题是因为,应用事件会通过
Spring
框架的事件推送机制发送,该机制会保证一个context
和他的子女父母context
都被通知。
也可以实现ApplicationListener<ApplicationEvent>
接口自动注入类来注册监听。
@Component
@Slf4j
public class ShutdownHookDemo implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
log.info("shutdown hook, ContextClosedEvent");
}
}
5. Web Environment
代码第七行
-
如果使用
Spring MVC
,那么会使用AnnotationConfigServletWebServerApplicationContext
; -
如果不使用
Spring MVC
而是使用Spring WebFlux
,那么会使用AnnotationConfigReactiveWebServerApplicationContext
; -
其他情况,使用
AnnotationConfigApplicationContext
;
可以自行重载Web应用类型,类型代码如下:
- NONE,不会启动为web应用,也不会启动嵌入的web服务;
- SERVIET,作为一个
servlet-based
web服务启动,会启动一个嵌入式的servlet
web服务,默认选项,Spring MVC
使用这个选项; - REACTIVE,作为一个
reactive-based
web服务启动,会启动一个嵌入式的reactive
web服务,Webflux
使用这个选项;
也可以通过
代码第十行
可以通过调用setApplicationContextFactory(…)
,实现对于应用上下文ApplicationContext
的完全控制。
6. Application可用性
代码第四行,可用性的改变也会出发事件
两个可用性状态Liveness
和Readiness
,注意,即使出现了错误,应用此时还是在运行的。
Liveness
是应用的内部运行状态,如果是BROKEN
代表应用内部已经发生问题且无法恢复,需要重启,CORRECT
代表正常。
Readiness
是指应用是否准备好了接受通信请求,ACCEPTING_TRAFFIC
状态代表ok。REFUSING_TRAFFIC
状态代表拒绝通信,在执行CommandLineRunner
和ApplicationRunner
时会出现,或者是应用过度繁忙时也会出现。
可用性状态可以用来配合K8S完成健康监测。
7. shutdown hook
1. JVM自带的shutdownHook
Runtime.getRuntime().addShutdownHook(new Thread(() -> log.info("shutdown hook, jvm demo")));
2. 监听Spring的ContextCloseEvent
@Component
@Slf4j
public class ShutdownHookDemo implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("shutdown hook, ContextClosedEvent");
}
}
3. 实现DisposableBean接口
@Component
@Slf4j
public class ShutdownHookDemo implements DisposableBean {
@Override
public void destroy() {
log.info("shutdown hook, disposable bean");
}
}
4. 使用注解@PreDestory
使用ApplicationRunner
或者CommandLineRunner
,或者使用spring.factories
进行注入,否则注入不会生效。
@Slf4j
@Component
public class TestApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("test");
}
@PostConstruct
public void init() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> log.error("shutdown hook, jvm runtime hook")));
}
@PreDestroy
public void preDestroy() {
log.info("shutdown hook, pre destroy");
}
}
8. Application Startup Tracking
track应用启动。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
}
可选ApplicationStartup
类型,
The first available implementation, FlightRecorderApplicationStartup
is provided by Spring Framework. It adds Spring-specific startup events to a Java Flight Recorder session and is meant for profiling applications and correlating their Spring context lifecycle with JVM events (such as allocations, GCs, class loading…). Once configured, you can record data by running the application with the Flight Recorder enabled:
Spring Boot ships with the BufferingApplicationStartup
variant; this implementation is meant for buffering the startup steps and draining them into an external metrics system. Applications can ask for the bean of type BufferingApplicationStartup
in any component.
参考
[1] Springboot doc