系列文章目录和关于我

一丶前言

在 《SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的》中我们学习了SpringBoot自动装配如何实现的,在 《Spring源码学习笔记12——总结篇IOC,Bean的生命周期,三大扩展点》我们总结了Spring IOC的底层原理。

但是我们还是不知道SpringApplication.run(主类.class, args)到底做了哪些事情。本文将和大家一起看看SpringBoot启动的大致流程,探讨SpringBoot留给我们的扩展接口

二丶SpringBoot启动流程分析

上面是SpringBoot调用SpringApplication.run(主类.class, args)启动的源码,源码并不复杂,整体流程大概如下

下面我们依据此图,看看这些步骤SpringBoot底层源码

1.获取SpringApplicationRunListener实现类,包装成SpringApplicationRunListeners

  • SpringApplicationRunListener是SpringBoot框架中的监听器,在SpringBoot启动到达对应阶段的时候,会回调starting,started等方法。

    为什么SpringBoot不适应Spring 里面的ApplicationListener昵,因为ApplicationListener依赖于Spring容器,@EventListener注解需要EventListenerMethodProcessor这个BeanFactoryPostProcessor扫描,将对应的bean和方法包装成ApplicationListener注册到ApplicationContext中(最终注册到ApplicationEventMulticaster事件多播器中)对于ApplicationListener类型bean则直接走注册到ApplicationContext的流程,整个流程只有Spring 容器启动后才能进行,如果没有SpringApplicationRunListener则开发者无法在SpringBoot启动对应阶段进行一些扩展逻辑的回调。

  • SpringApplicationRunListeners 可以看成是SpringApplicationRunListener的门面(门面设计模式)

其使用List<SpringApplicationRunListener>持有所有的SpringApplicationRunListener,然后starting等方法都是循环调用,集合中SpringApplicationRunListener对应的方法

  • SpringBoot如何获取所有的SpringApplciationListener

    这里将从META-INF/spring.factories获取org.springframework.boot.SpringApplicationRunListener 定义的实现类全限定类名,然后反射调用构造方法(SpringApplication application, String[] args)进行实例化。随后将根据@Order 或者 Ordered接口定义的顺序进行排序,然后包装成SpringApplicationRunListeners

    注意无法使用@Component注解 标注在SpringApplciationListener注解上,来实现事件的监听,必须在META-INF/spring.factories中定义,并且必须具备构造方法(SpringApplication application, String[] args)

  • EventPublishingRunListener

    SpringApplication#addListeners 允许我们注册ApplicationListener到SpringBoot中,然后EventPublishingRunListener其内部会new 一个简单的事件多播器SimpleApplicationEventMulticaster,在对应的SpringBoot启动阶段,推送事件。下面式如何注册ApplicationListener

    注意这些ApplicationListener不会被注册到Spring上下文中,意味着不会响应Spring上下文推送的事件,除非这个ApplicationListener是一个Spring Bean 并且被Spring管理。

    下图是EventPublishingRunListener在SpringBoot启动的不同阶段,推送事件

2.SpringApplicationListeners#starting

没啥好说的,循环回调SpringApplicationRunListener#starting方法

3.prepareEnvironment 根据项目选择Environment实现类,并实例化

在这一步,SpringBoot会根据类路径中的类选择一个Environment并实例化,并且根据当前激活的配置,选择对应的配置文件,进行解析,并保存到Environment中。下面是SpringBoot选择Environment的源码

那么SpringBoot是如何判断当前项目是什么应用类型昵?

其实根据类路径下是否具备指定的类,然后得到指定类型,一般我们都是servlet应用,会选择StandardServletEnvironment

4.SpringApplicationListeners#environmentPrepared

2.SpringApplicationListeners#starting

5.createApplicationContext

根据类路径指定类推断使用什么ConfigurableApplicationContext(一般servlet应用使用AnnotationConfigServletWebServerApplicationContext)然后实例化AnnotationConfigServletWebServerApplicationContext

AnnotationConfigServletWebServerApplicationContext#onRefresh方法在Spring容器刷新后会被调用,这个方法将启动Tomcat内嵌服务器

6.prepareContext

这个方法主要会做以下操作

  • 回调ApplicationContextInitializer#initialize
  • 回调所有SpringApplicationRunListener#contextPrepared
  • 将主类包装成BeanDefinition,注册到Spring容器上下文中
  • 回调所有SpringApplicationRunListener#contextLoaded

利用SpringApplicationRunListeners回调SpringApplicationRunListener,同2,不在赘述

6.1从META-INFO/spring.factories中拿所有ApplicationContextInitializer然后回调initialize方法

在spring上下文refresh方法调用前,会回调initialize方法

这里调用前还会判断ApplicationContextInitializer定义的泛型,保证5这一步创建的上下文,符合泛型的要求

6.2 将主类包装成BeanDefinition,注册到Spring容器上下文中

这一步非常重要,主类上的注解@SpringBootApplication需要ConfigurationClassPostProcessor解析,才能发挥@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor处理主类的前提是主类的BeanDefinition需要在Spring容器中。

也就是说SpringBoot的自动装配,和扫描包路径下的Spring 组件的前提是,主类的BeanDefinition在Spring容器中

这里的BeanDefinitionRegistry,其实就是来自5这一步的ApplicationContext,一般来说AnnotationConfigServletWebServerApplicationContext内部持有了一个DefaultListableBeanFactory,DefaultListableBeanFactoryBeanDefinitionRegistry的实现类,其底层使用一个ConcurrentHashMap维护,key是bean的名称,value是对应的BeanDefinition

当资源是一个Class的时候,会使用AnnotatedBeanDefinitionReader读取Class对象,生成BeanDefinition

这一步还支持xml的方式

7.回调SpringApplicationRunListener#contextLoaded

同2

8.刷新Spring容器上下文

Spring源码学习笔记12——总结篇IOC,Bean的生命周期,三大扩展点》这篇博客做了详细的分析

这里会进行自动装配和包路径扫描注册BeanDefinition,然后实例化单例bean

9.回调SpringApplicationRunListener#started

同2

10.callRunners

从spring容器中拿到ApplicationRunner,和CommandLineRunner调用run方法

三丶SpringApplication,ApplicationContext,BeanFactory 三平面

我们将SpringApplication看作是SpringBoot平面,ApplicationContext看作是Spring平面,BeanFactory看作是Bean工厂平面,SpringBoot启动到触发spring容器刷新,然后触发BeanFactory实例化所有单例,非懒加载bean的流程如下

SpringBoot源码学习3——SpringBoot启动流程的更多相关文章

  1. SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

    系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...

  2. SpringBoot源码学习系列之启动原理简介

    本博客通过debug方式简单跟一下Springboot application启动的源码,Springboot的启动源码是比较复杂的,本博客只是简单梳理一下源码,浅析其原理 为了方便跟源码,先找个Ap ...

  3. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  4. ThinkPHP5.0源码学习之框架启动流程

    ThinkPHP5框架的启动流程图如下: ThinkPHP5的启动流程按照文件分为三步: 1.请求入口(public/index.php) 2.框架启动(thinkphp/start.php) 3.应 ...

  5. SpringBoot源码学习系列之嵌入式Servlet容器

    目录 1.博客前言简单介绍 2.定制servlet容器 3.变换servlet容器 4.servlet容器启动原理 SpringBoot源码学习系列之嵌入式Servlet容器启动原理 @ 1.博客前言 ...

  6. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  7. SpringBoot源码学习系列之异常处理自动配置

    SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...

  8. Cocos2dx源码赏析(1)之启动流程与主循环

    Cocos2dx源码赏析(1)之启动流程与主循环 我们知道Cocos2dx是一款开源的跨平台游戏引擎,而学习开源项目一个较实用的办法就是读源码.所谓,"源码之前,了无秘密".而笔者 ...

  9. 【spring-boot 源码解析】spring-boot 依赖管理梳理图

    在文章 [spring-boot 源码解析]spring-boot 依赖管理 中,我梳理了 spring-boot-build.spring-boot-parent.spring-boot-depen ...

  10. JVM源码分析之JVM启动流程

      原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十四篇. 今天呢!灯塔君跟大家讲: JVM源码分析之JVM启动流程 前言: 执行Java类的main方法,程序就能运 ...

随机推荐

  1. C语言基础--数组

    数组 概念:在内存中连续存储的具有相同数据类型的一组数据的集合. 注意: 数组中的数据类型必须都是一致的 数组在内存中必须是连续的存储空间 定义数组时候的注意事项: 定义数组的时候,[]里面的值不能是 ...

  2. NLP之基于BERT的预测掩码标记和句间关系判断

    BERT @ 目录 BERT 程序步骤 程序步骤 设置基本变量值,数据预处理 构建输入样本 在样本集中随机选取a和b两个句子 把ab两个句子合并为1个模型输入句,在句首加入分类符CLS,在ab中间和句 ...

  3. 【一】ERNIE:飞桨开源开发套件,入门学习,看看行业顶尖持续学习语义理解框架,如何取得世界多个实战的SOTA效果?

    ​ 参考文章: 深度剖析知识增强语义表示模型--ERNIE_财神Childe的博客-CSDN博客_ernie模型 ERNIE_ERNIE开源开发套件_飞桨 https://github.com/Pad ...

  4. 四、Django中使用celery

    项目跟目录创建celery包,目录结构如下: mycelery/ ├── config.py ├── __init__.py ├── main.py └── sms/ ├── __init__.py ...

  5. Python 多重继承时metaclass conflict问题解决与原理探究

    背景 最近有一个需求需要自定义一个多继承abc.ABC与django.contrib.admin.ModelAdmin两个父类的抽象子类,方便不同模块复用大部分代码,同时强制必须实现所有抽象方法,没想 ...

  6. go基础语法50问,来看看你的go基础合格了吗?

    目录 1.使用值为 nil 的 slice.map会发生啥 2.访问 map 中的 key,需要注意啥 3.string 类型的值可以修改吗 4.switch 中如何强制执行下一个 case 代码块 ...

  7. ES 客户端 RestHighLevelClient Connection reset by peer 亲测有效 2022-11-05

    导读 最新公司ES集群老出现连接关闭,进而导致查询|写入ES时报错,报错日志显示如下 [2m2022-10-23 14:13:10.088[0;39m - [31mERROR[0;39m - [35m ...

  8. Oracle:ORA-00911: invalid character解决办法

    问题记录:用jmeter执行sql语句,报错:ORA-00911: invalid character. 解决方法:sql语句末尾";"导致,去掉即可解决. 过程记录: 使用jme ...

  9. [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)

    [数据结构-线性表1.2] 链表与 LinkedList<T> [注:本篇文章源码内容较少,分析度较浅,请酌情选择阅读] 关键词:链表(数据结构)    C#中的链表(源码)    可空类 ...

  10. threejs三维地图大屏项目分享

    这是最近公司的一个项目.客户的需求是基于总公司和子公司的数据,开发一个数据展示大屏. 大屏两边都是一些图表展示数据,中间部分是一个三维中国地图,点击中国地图的某个省份,可以下钻到省份地图的展示. 地图 ...