启动类
Spring 启动类主要是 Annotation (@SpringBootApplication) 和 main 方法体中的 SpringApplication.run。
其中注解 @SpringBootApplication 是一个统一注解,定义了 SpringBoot 的自动扫描注入等行为, 
SpringApplication.run 是一个 static 方法,SpringBoot 启动的实际方法。

  1. 1 @SpringBootApplication
  2. 2 public class Application {
  3. 3 public static void main(String[] args) {
  4. 4 SpringApplication.run(Application.class, args);
  5. 5 }
  6. 6 }
Annotation
 
SpringBoot 启动的第一个注解,从 @SpringBootApplication 开始,
 
它包含了 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 等主要注解,
 
其中 @Target、@Retention、@Documented 是元注解,@Inherited 表明子类会继承父类中被此注解修饰的注解。
 
 
 
@SpringBootConfiguration
 
此注解的官方文档说明:表示一个类提供了 Spring Boot 应用程序 @Configuration
 
可以用作 Spring 标准 @Configuration 注释的替代方法,以便可以自动找到配置(例如在测试中)。
 
应用程序应该只包含一个 @SpringBootConfiguration 并且大多数惯用的 Spring Boot 应用程序将从 @SpringBootApplication 继承它。
 
实际上就是跟 @Configuration 的作用相同的,区别就是 @SpringBootConfiguration 允许自动寻找配置,二者都是将当前类中用 @Bean 标记的方法的实例加入 Spring 容器。
 
@EnableAutoConfiguration
 
此注解的官方文档说明:启用 Spring 应用程序上下文的自动配置,尝试猜测和配置您可能需要的 bean
 
自动配置类通常根据您的类路径和您定义的 bean 来应用。
 
实际上就是开启了 SpringBoot 的自动配置功能
 
它还包括子注解 @AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class),
 
这两个注解中前者的作用就是将添加该注解的所在类的 package 作为自动配置 package 进行管理,
 
意思就是扫描包时,将从当前类的包向下扫描,这也是为什么要把启动类放到最外层的原因。
 
而后者 @Import 就是把 AutoConfigurationImportSelector 前置类,用于扫描到的需要自动配置的注入到 ApplicationContext 中
 
@ComponentScan
 
此注解的官方文档说明:配置用于Configuration类的组件扫描指令。如果没有定义特定的包,将从声明该注解的类的包开始扫描。
 
注解配置总结:
 
SpringBoot 启动类上的这5个注解,分别有各自不同的功能,实现自动配置注入。总结一下就是:
 
@SpringBootConfiguration 注解声明启动类为配置类,将其替换为 @Configuration 也可以,在启动 时将 @Bean 注解标记的方法注入容器,Bean 名称就是方法名;
 
@EnableAutoConfiguration 注解开启 Spring 的自动配置,其原理就是执行自动配置前会调用 AnnotationScanner 检查启动类上标记的注解是否存在 @EnableAutoConfiguration ,
 
没有就会抛出参数异常:llegalArgumentException: No auto-configuration attributes found.
 
@AutoConfigurationPackage 注解标记启动类所在包为扫描的起始包;
 
@CompnentScan 注解从起始包向下扫描配置类;
 
@Import 导入的 AutoConfigurationImportSelector 类会调用 SpringFactoriesLoader 读取配置文件 spring.factories
 
把需要自动配置的 Bean 初始化并注入到 ApplicationContext 。
 
实际上参与自动配置注入的注解有4个:@EnableAutoConfiguration、@AutoConfigurationPackage、@Import 和 @CompnentScan,而 @SpringBootConfiguration 对启动和自动配置并无影响
 
 
SpringBootApplication
 
SpringBoot 启动的引导类,以下为官方文档的说明:
 

对 SpringBootApplication.run 的源码分析如下:
  1. public ConfigurableApplicationContext run(String... args) {
  2.  
  3. // 任务执行计时监听器
  4. StopWatch stopWatch = new StopWatch();
  5. stopWatch.start();
  6. ConfigurableApplicationContext context = null;
  7. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  8. // 设置系统java.awt.headless属性,确定是否开启headless模式(默认开启headless模式)
  9. configureHeadlessProperty();
  10. // SpringApplication run方法的监听器,通过 SpringFactoriesLoader 加载
  11. SpringApplicationRunListeners listeners = getRunListeners(args);
  12. // 启动监听器
  13. listeners.starting();
  14. try {
  15.  
  16. // 创建 SpringBoot 默认启动参数
  17. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  18. // 用于设置活动和默认配置文件以及操作基础属性源的工具,即配置启动环境
  19. ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  20. // 配置对不存在的BeanInfo类的重复 ClassLoader 访问
  21. configureIgnoreBeanInfo(environment);
  22. // 打印LOGO
  23. Banner printedBanner = printBanner(environment);
  24. // 创建合适的 ApplicationContext
  25. context = createApplicationContext();
  26. // SpringBoot 启动错误的回调接口,通过 SpringFactoriesLoader 加载
  27. exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  28. new Class[] { ConfigurableApplicationContext.class }, context);
  29. // 准备 Spring 上下文
  30. // 在这个方法中,主要完成了以下几件事:
  31. // 1、设置SpringBoot的环境配置(Environment)
  32. // 2、注册Spring Bean名称的序列化器BeanNameGenerator,并设置资源加载器ResourceLoader
  33. // 3、加载ApplicationContextInitializer初始化器,并进行初始化
  34. // 4、统一将上面的Environment、BeanNameGenerator、ResourceLoader使用默认的Bean注册器进行注册
  35. prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  36. // 注册销毁上下文的钩子
  37. // 刷新 Spring 上下文
  38. refreshContext(context);
  39. // 刷新后处理
  40. afterRefresh(context, applicationArguments);
  41. // 停止计时监听器
  42. stopWatch.stop();
  43. if (this.logStartupInfo) {
  44. // 打印计时器日志
  45. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  46. }
  47. // 上下文已刷新且应用程序已启动
  48. listeners.started(context);
  49. // 运行 ApplicationRunner 和 CommandLineRunner 的实现类
  50. // 二者区别在于入参不同
  51. callRunners(context, applicationArguments);
  52. }
  53. catch (Throwable ex) {
  54. handleRunFailure(context, ex, exceptionReporters, listeners);
  55. throw new IllegalStateException(ex);
  56. }
  57.  
  58. try {
  59. // 发布事件以指示应用程序已准备好上下文环境为请求提供服务
  60. listeners.running(context);
  61. }
  62. catch (Throwable ex) {
  63. handleRunFailure(context, ex, exceptionReporters, null);
  64. throw new IllegalStateException(ex);
  65. }
  66. return context;
  67. }
在执行 refreshContext(context); 时,实际上是刷新了 ApplicationContext 上下文资源,根据代码追踪如下:
  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // 准备此上下文以进行刷新。
  5. prepareRefresh();
  6.  
  7. // 解析所有 Spring 配置文件,
  8. // 将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,
  9. // 加载到 BeanFactory 中
  10. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  11.  
  12. // 配置 beanFactory 的标准上下文特征,
  13. // 例如上下文的 ClassLoader、后置处理器等
  14. prepareBeanFactory(beanFactory);
  15.  
  16. try {
  17. // 允许在上下文子类中对 BeanFactory 进行后续处理,
  18. // 默认实现为空,留给子类实现。
  19. postProcessBeanFactory(beanFactory);
  20.  
  21. // 实例化和调用所有 BeanFactoryPostProcessor
  22. // BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,
  23. // Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。
  24. invokeBeanFactoryPostProcessors(beanFactory);
  25.  
  26. // 注册拦截 BeanPostProcessor,
  27. // 将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
  28. // BeanFactoryPostProcessor 是针对 BeanFactory 的扩展,
  29. // 主要用在 bean 实例化之前,读取 bean 的定义,并可以修改它。
  30. registerBeanPostProcessors(beanFactory);
  31.  
  32. // 初始化此上下文的消息源。
  33. initMessageSource();
  34.  
  35. // 为此上下文初始化应用的事件广播器 ApplicationEventMulticaster
  36. initApplicationEventMulticaster();
  37.  
  38. // 初始化特定上下文子类中的其他特殊bean ,默认实现为空。
  39. onRefresh();
  40.  
  41. // 检查监听器bean并注册它们。
  42. registerListeners();
  43.  
  44. // 实例化所有剩余的(非懒加载)单例。
  45. finishBeanFactoryInitialization(beanFactory);
  46.  
  47. // 最后一步:发布相应的事件。
  48. // 完成此上下文的刷新,主要是推送上下文刷新完毕事件(ContextRefreshedEvent )到监听器。
  49. finishRefresh();
  50. }
  51.  
  52. catch (BeansException ex) {
  53. if (logger.isWarnEnabled()) {
  54. logger.warn("Exception encountered during context initialization - " +
  55. "cancelling refresh attempt: " + ex);
  56. }
  57.  
  58. // 销毁已创建的单例以避免闲置资源(清理不被引用的 Bean)。
  59. destroyBeans();
  60.  
  61. // 重置“active”标志。
  62. cancelRefresh(ex);
  63.  
  64. // 将异常传播到调用方。
  65. throw ex;
  66. }
  67.  
  68. finally {
  69. // 重置Spring核心中的常见内省缓存,因为我们
  70. // 可能不再需要单例bean的元数据了。。。
  71. resetCommonCaches();
  72. }
  73. }
  74. }
构建 IoC 构成中最重要的4个方法:
 
obtainFreshBeanFactory 创建一个新的 BeanFactory、读取和解析 bean 定义。
 
invokeBeanFactoryPostProcessors 提供给开发者对 BeanFactory 进行扩展。
 
registerBeanPostProcessors 提供给开发者对 bean 进行扩展。
 
finishBeanFactoryInitialization 实例化剩余的所有非懒加载单例 bean。
 
与一般的应用程序一样,SpringBoot 的启动基本流程也是:启动事件监听、配置环境、初始化Spring上下文、注入Bean。
 
其中作为自动配置的 AutoConfigurationImportSelector 类的主要方法 getAutoConfigurationEntry -> getAutoConfigurationImportFilters 调用 SpringFactoriesLoader.loadFactories 读取配置、初始化 Bean factory,
 
在 SpringBootApplication.run 方法执行到 refreshContext(context); 时注入 ApplicationContext。
  1. protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
  2. return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
  3. }
 
对 SpringFactoriesLoader.loadFactories 源码分析如下
  1. public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
  2. Assert.notNull(factoryType, "'factoryType' must not be null");
  3. ClassLoader classLoaderToUse = classLoader;
  4. if (classLoaderToUse == null) {
  5. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  6. }
  7. // 加载配置文件 META-INF/spring.factories,获取自动配置的类路径,即加载 Resource 资源
  8. List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
  9. if (logger.isTraceEnabled()) {
  10. logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
  11. }
  12. List<T> result = new ArrayList<>(factoryImplementationNames.size());
  13. for (String factoryImplementationName : factoryImplementationNames) {
  14. // 通过反射初始化 Bean factory
  15. result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
  16. }
  17. AnnotationAwareOrderComparator.sort(result);
  18. return result;
  19. }
 
 
启动流程分析图
 

 
断点调试参考位点

-------------------------------------------------------------------------------------- 分割线-----------------------------------------------------------------------------

代码小白,首次发文,望大佬不吝赐教!

Springboot 启动分析的更多相关文章

  1. SpringBoot无废话入门02:SpringBoot启动分析

    1.核心注解 在上文中,我们讲到了@SpringBootApplication是SpringBoot的核心注解. 可以很方便的在idea中下载源码来查看该注解的源码,如下: 可以看到,该注解本身又被其 ...

  2. SpringBoot启动原理分析

    用了差不多两年的SpringBoot了,可以说对SpringBoot已经很熟了,但是仔细一想SpringBoot的启动流程,还是让自己有点懵逼,不得不说是自己工作和学习的失误,所以以此文对Spring ...

  3. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  5. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. SpringBoot启动流程分析(二):SpringApplication的run方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  7. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  8. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  9. 深度好文,springboot启动原理详细分析

    我们开发任何一个Spring Boot项目,都会用到如下的启动类 1 @SpringBootApplication 2 public class Application { 3 public stat ...

随机推荐

  1. 【大咖直播】Elastic 企业搜索实战工作坊(第一期)

    借助 App Search 提供的内置功能,您可轻松打造卓越的搜索体验.直观的相关度调整以及开箱即用的搜索分析,不仅可以优化所提供的内容,其提供的 API 还可帮助您将位于各处的所有内容源关联在一起. ...

  2. Java面向对象编程(二)

    关键字 -- this 一.this关键字的使用: 1.this可以用来修饰.调用:属性.方法.构造器. 2.this修饰属性和方法: this理解为:当前对象 或 当前正在创建的对象. 2.1 在类 ...

  3. NOIP模拟74

    前言 我就想说一句,T3 给了一个什么牛马大样例!!!!!!!!,气\(^{TM}\)死我!!!!!!! 我的 \(\mathcal{O}(n)\) 算法始终过不掉大样例我 TM ,要不然我就直接上矩 ...

  4. 痞子衡嵌入式:i.MXRT全系列下FlexSPI外设AHB Master ID定义与AHB RX Buffer指定的异同

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT全系列下FlexSPI外设AHB Master ID定义与AHB RX Buffer指定的异同. 因为 i.MXRT 全系列 ...

  5. javascriptRemke之原型的重要性

    前言:JavaScript的原型对象一直是新人学习js的一大重大阻碍,但是原型的知识往往又是面试中常常会被深挖的一个点,为什么会这样呢?本文带你揭秘JavaScript原型的重要性,了解重要性之后再进 ...

  6. 2020.10.16--vj个人赛补题

    D - Drinks Choosing Old timers of Summer Informatics School can remember previous camps in which eac ...

  7. Golang通脉之流程控制

    流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的"经脉". Go语言中最常用的流程控制有if和for,而switch和goto主要是为了简化代码. ...

  8. 3.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程

    3.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程 The figure below shows a typical reques ...

  9. 手把手教你学Dapr - 1. .Net开发者的大时代

    Dapr全称 Distributed Application Runtime,分布式应用运行时 Dapr的口号 简化云原生应用开发,聚焦在应用的核心逻辑,让代码简单.可移植 Dapr的目标 最佳实践的 ...

  10. Beta阶段第八次会议

    Beta阶段第八次会议 时间:2020.5.24 完成工作 姓名 工作 难度 完成度 ltx 1.修改一下小程序游客模式的风格 轻 80% xyq 1.针对昨天提出的意见对场地申请表格进行修改 中 9 ...