Spring Boot 2 特性


摘自官网

  • Create stand-alone Spring applications
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
  • Provide opinionated 'starter' dependencies to simplify your build configuration
  • Automatically configure Spring and 3rd party libraries whenever possible
  • Provide production-ready features such as metrics, health checks and externalized configuration
  • Absolutely no code generation and no requirement for XML configuration

前言


目前 spring boot 已被广泛使用,关于 spring boot 的文章更是随处可见。因此,我们就串联琐碎的知识点,简单整理出一条线。使用spring boot 已经有一段时间了,但是有些知识点还是比较模糊。它启动过程都干了什么?如何实现自动配置的?启动 web 应用的时候什么时候启动 tomcat 服务的?发起一个 http 请求会经历什么过程?

启动过程


spring boot 启动过程的源码分析网上有很多,而且比较详细,就不再详细赘述了。有兴趣的同学自行翻阅资料哈~

在 spring boot 启动的时候,会根据工程的环境(主要是根据特定的类名称)确定一个 ConfigurableApplicationContext 的上下文对象,我们就以 AnnotationConfigServletWebServerApplicationContext 做例子。

// 创建ConfigurableApplicationContext,如果是servlet环境会创建AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
// 从spring.factories中获取SpringBootExceptionReporter工厂,并初始化spring boot的异常分析对象
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新appContext,这里开始启动spring管理bean
refreshContext(context);
// afterRefresh方法是protected的空实现,我们可以用这个钩子做一些事情
afterRefresh(context, applicationArguments);

重点关注 refreshContext 方法,这个方法强制转换对象为 AbstractApplicationContext 后执行他的 refresh 方法

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

经常看 spring 源码的同学,看到这一步应该就比较熟悉了,和我们传统的 SSM 框架一样的启动流程。

启动过程中功能简述

  • 通过 SpringFactoriesLoader 提供通用的工厂加载机制,具体是解析工程下所有jar包中的 META-INF/spring.factories 文件,并提供反序列化 bean 功能
  • 提供 PropertiesYaml 配置文件的解析,以及数据绑定
  • 推断上下文环境是 SERVLETREACTIVENON, 以创建相应的 ConfigurableApplicationContext 来管理 Bean 的生命周期
  • 支持可扩展 SPI 接口,包括 PropertySourceLoaderSpringApplicationRunListenerSpringBootExceptionReporterApplicationContextInitializerApplicationListenerEnvironmentPostProcessorFailureAnalyzerFailureAnalysisReporter

了解了 SpringFactoriesLoader 实现类似 java spi 的功能,我们就可以自定义实现 SPI 的接口。实现方式:

  1. 以 java config 形式配置托管,例如:@Component
  2. 在执行 run 返回的 ConfigurableApplicationContext 中添加(部分接口可以)
  3. 工程中新建 META-INF/spring.factories 文件,以 接口=实现 的形式存放

实现自动配置


我们的 spring boot 的启动类通常会使用 @SpringBootApplication ,该注解包含了@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan,而 @EnableAutoConfiguration 是实现自动配置的关键

...
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

在前面我们提到 spring boot 启动会执行到AbstractApplicationContext.refresh(),之后调用ConfigurationClassPostProcessor.processConfigBeanDefinitions扫描所有的 @Import ,并执行 AutoConfigurationImportSelectorselectImports,该方法调用 SpringFactoriesLoader 读取spring.factories获取 EnableAutoConfiguration 对应的实现类。

方法执行栈
refresh --> invokeBeanFactoryPostProcessors --> ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry --> AutoConfigurationImportSelector.selectImports

最后会在 AbstractApplicationContext.finishBeanFactoryInitialization 初始化非懒加载的对象,完成自动化配置。

Web 服务的启动


首先我们来看下 AnnotationConfigServletWebServerApplicationContext 类关系图,由于图比较大就不放出来了。
AnnotationConfigServletWebServerApplicationContext --> ServletWebServerApplicationContext --> GenericWebApplicationContext --> GenericApplicationContext --> AbstractApplicationContext
所以在执行 AbstractApplicationContext.refresh 中调用 onRefresh 时,会调用 ServletWebServerApplicationContext.onRefresh
再调用 createWebServer

// ServletWebServerApplicationContext.createWebServer
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    //如果配有配置WebServer,则从BeanFactory中获取一个ServletWebServerFactory的实例
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            // 调用selfInitialize,配置servletContext,注册servletContext到spring context中,并调用ServletContextInitializer的onStartup钩子方法
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    // 初始化servletContext配置
    initPropertySources();
}

总结


最后,我们通过一图简单解答前言中提到的问题。

springboot.png

当 spring boot 启动完成后,正常情况下一次 http 请求大致会经历这样一个过程。用户输入访问地址 --> 浏览器客户端发起请求 --> web 服务(tomcat、jetty)转发请求 --> 请求处理(servlet规范、reactive规范、其他自定义的) --> 响应结果

注:第一次发博客,难免有所不足,甚至出现错误的地方,欢迎大家指正,谢谢。


飞奔的企鹅
4 声望0 粉丝

难得糊涂