系列文章目录和关于我

一丶前言

在 《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. 重新整理 .net core 实践篇 ———— linux 上线篇 [外篇]

    前言 简单整理一个linux 简单上线. 这个是该系列的外篇,该系列继续更新.献给刚学的人. 正文 安装实例 dotnet new webapp -n AspNetCoreDemo -o firstw ...

  2. Linux正则表达式与grep

    bash是什么 bash是一个命令处理器,运行在文本窗口中,并能执行用户直接输入的命令 bash还能从文件中读取linxu命令,称之为脚本 bash支持通配符.管道.命令替换.条件判断等逻辑控制语句 ...

  3. Scrapy 如何传递 get请求的params

    我们都知道 在requests中可以使用 requests.get(url,params)的方式传值 那么在scrapy中如何传值呢 直接看代码 from urllib.parse import ur ...

  4. 前端html和css总结

    1.html知识总结 1.1 表格的的相关属性 属性 表示 border-collapse 设置表格的边框是否被合并为一个单一的边框 cellpadding 单元格边距 cellspacing 单元格 ...

  5. JAVA代码审计之xss

    java_sec_code xss 补充和回忆一下一些开发基础 @RestController @RequestMapping(value = "/xss") public cla ...

  6. Mybatis:解决调用带有集合类型形参的mapper方法时,集合参数为空或null的问题

    此文章有问题,待修改! 使用Mybatis时,有时需要批量增删改查,这时就要向mapper方法中传入集合类型(List或Set)参数,下面是一个示例. // 该文件不完整,只展现关键部分 @Mappe ...

  7. 【Devexpress】Gridcontorl动态创建列不显示的问题

    通过代码创建列但是不显示,这个原因是因为代码创建的列Visible属性默认是false 所以需要设置为true就会显示了 gridColumn.Visible = true;

  8. 动态规划篇——DP问题

    动态规划篇--DP问题 本次我们介绍动态规划篇的DP问题,我们会从下面几个角度来介绍: 区间DP 计数DP 树状DP 记忆化搜索 区间DP 我们通过一个案例来讲解区间DP: /*题目展示*/ 题目名: ...

  9. java基础知识-lambda表达式

    一.什么是lambda? 在Java中,我们可以将一个值赋值给一个Java变量. int aValue = 129; String aString = "hello world"; ...

  10. 函数调用时用const保护指针

    当调用函数并且把指向变量的指针作为参数传入时,通常会假设函数将修改变量(否则,为什么函数需要指针呢?).例如,如果在程序中看到语句 f(&x); 大概是希望f改变x的值.但是,f仅需检查x的值 ...