SpringBoot一站式启动流程源码分析

2021-03-12 00:31

阅读:484

标签:gen   property   开放   特征   分析   自定义   技术   流程   and   

一、前言

技术图片
  
由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码。那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻SpringBoot的一站式启动流程。
  其实SpringApplication 是将一个典型的Spring应用的启动流程”模板化”了,在没有特殊定制需求的情况下,默认的模板化后的执行流程就能满足我们的需求了。即便是我们有了特殊的需求也没有太大关系,SpringApplication在内部合适的启动节点给我们提供了一系列不同类型的扩展点,我们就可以通过这些开放的扩展点来对SpringBoot程序的启动和关闭过程来进行定制和扩展。

二、关于定制

技术图片

SpringApplication中提供的最简单的定制方式当属设置方法(Setters)定制了。例如,我们可以把启动类改成如下的方式来扩展启动行为:
技术图片
大多数的情况下,SpringApplication默认已经提供好了设置,我们基本不需要再对这些表层进行研究了,对表象之下的本质才是我们最应该探究的课题。

三、揭秘SpringApplication的执行流程 

技术图片
因为启动程序的代码中运行的就是SpringApplication的run方法,所以我们执行流程当然就要从这个run方法开始,先上源码:
技术图片
可以看出,启动时:调用run方法先创建一个SpringApplication对象实例,然后调用创建好的SpringApplication的实例的run方法。在SpringApplication实例化的时候,它又会运行以下代码:
技术图片

  • 首先运行deduceWebEnvironment方法(代码中标记1处),该方法的作用是根据classpath里面是否存在某些特征类({“javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext” })来决定是创建一个Web类型的ApplicationContext还是创建一个标准Standalone类型的ApplicationContext.
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer(代码中标记2处)。
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener(代码中标记3处)。
  • 推断并设置main方法的定义类(代码中标记4处)。
    这样,SpringApplication就完成了实例化并且完成了设置。然后就开始执行SpringApplication实例的run方法的逻辑了:
    技术图片
  • 该方法中,首先通过SpringFactoriesLoader查找并加载SpringApplicationRunListener(代码标记1处),然后接着调用它们的started()方法(代码标记2处),告诉这些SpringApplicationRunListener说:“Hello, SpringBoot应用要开始执行喽”。
  • 接着,创建和配置当前SpringBoot应用将要使用的Environment(包括配置要使用到的PropertySource和Profile)(代码标记3处).
  • 然后遍历所有的SpringApplicationRunListener的environmentPrepared()方法,告诉他们:“当前SpringBoot应用使用的Environment已经准备好了哈”(代码标记4处)。
  • 如果SpringApplication的showBanner属性为true的话,则打印banner(这里是基于Banner.Mode来决定banner的打印行为)(代码标记5处)。这个步骤其实我们不用过多关心,个人感觉它的用途纯粹是为了好玩。
  • 根据用户是否明确设置了applicationContextClass类型以及初始化SpringApplication类阶段的推断结果,决定该为当前的SpringBoot应用创建什么类型的ApplicationContext,并完成创建(代码标记6处)。
  • 然后将之前准备好的Environment设置给创建好的ApplicationContext,供以后使用(代码标记7处)。
  • 根据条件来决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader(代码标记8处)。
  • 完成后,SpringApplication会再次借助SpringFactoriesLoader查找并加载classpath中所有可用的ApplicationContextInitializer,然后遍历调用它们的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理(代码标记9处)。
  • 接着,遍历所有SpringApplicationRunListener的contextPrepared()方法,通知它们:“SpringBoot应用的ApplicationContext准备好啦哈~”(代码标记10处)。
  • 非常最要的一步,将之前通过@EnableAutoConfiguration获取的所有配置类以及其他形式的IoC容器配置类加载到已经准备完毕的ApplicationContext中(代码标记11处)。
  • 遍历所有的SpringApplicationRunListener并调用它们的contextLoaded()方法,告诉所有的SpringApplicationRunListener说:“ApplicationContext装填完毕啦”(代码标记12处)。
  • 调用ApplicationContext的refresh()方法,完成IoC容器初始化的最后一步流程(代码标记13处)。
  • 然后再根据条件来决定是否需要添加ShutdownHook(代码标记14处)。
  • 查找当前ApplicationContext中是否注册有ApplicationRunner以及CommandLineRunner,如果有,则遍历执行它们。
  • 不出意外的情况下,遍历所有的SpringApplicationRunListener并执行finished()方法,告诉他们:“启动大功告成了!”(代码标记16处),如果整个启动过程中出现了异常,则依然调用所有的SpringApplicationRunListener的finished()方法,这种情况下会将所有的异常信息一起传入并处理(代码标记17处)。
    经过以上的这些步骤以后,一个完整的SpringBoot应用就启动完毕了!整个过程虽然看起来冗长无比,但其实很多都是一些事件通知的扩展点,如果我们将这些逻辑暂时的忽略掉的话,那整个SpringBoot应用启动的逻辑就可以压缩到极其精简的几步了,如下图:
     技术图片 

    这样我们对比以后就会发现,其实SpringApplication提供的这些各种扩展点有点”喧宾夺主”的味道,它们占据了整个SpringBoot应用启动逻辑的大部分,除了初始化准备好ApplicationContext,剩下的绝大部分工作均是通过这些扩展点来完成的。

四、总结

技术图片
  本文,我们通过源码的方式来解析了整个SpringBoot应用程序的启动过程,我们发现了大部分工作都是由SpringApplication提供的扩展点来完成的,那我们下一篇文章就来逐一解析这些扩展点组件,这样的话,我们就可以在需要的时候可以很轻松的为我所用!

SpringBoot一站式启动流程源码分析

标签:gen   property   开放   特征   分析   自定义   技术   流程   and   

原文地址:https://blog.51cto.com/15047484/2560246


评论


亲,登录后才可以留言!