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加载时间,在需要时才会加载,可以降低应用启动时间。

可能的问题

  1. 推迟了问题被发现的时间,因为加载时间推迟了;

使用方式:

  1. SpringApplicationBuilderlazyInitialization()方法;
  2. SpringApplicationsetLazyInitialization方法;
  3. 配置文件增加: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-basedweb服务启动,会启动一个嵌入式的servletweb服务,默认选项,Spring MVC使用这个选项;
  • REACTIVE,作为一个reactive-basedweb服务启动,会启动一个嵌入式的reactiveweb服务,Webflux使用这个选项;

也可以通过

代码第十行

可以通过调用setApplicationContextFactory(…),实现对于应用上下文ApplicationContext的完全控制。

6. Application可用性

代码第四行,可用性的改变也会出发事件

两个可用性状态LivenessReadiness,注意,即使出现了错误,应用此时还是在运行的。

Liveness是应用的内部运行状态,如果是BROKEN代表应用内部已经发生问题且无法恢复,需要重启,CORRECT代表正常。

Readiness是指应用是否准备好了接受通信请求,ACCEPTING_TRAFFIC状态代表ok。REFUSING_TRAFFIC状态代表拒绝通信,在执行CommandLineRunnerApplicationRunner时会出现,或者是应用过度繁忙时也会出现。

可用性状态可以用来配合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

[2] SpringBoot下实现Shutdown Hook的几种方式