Spring Boot系列(四):Spring Boot源码解析
一、自动装配原理
之前博文已经讲过,@SpringBootApplication继承了@EnableAutoConfiguration,该注解导入了AutoConfigurationImport Selector,这个类主要是扫描spring-boot-autoconfigure下面的META-INF\spring.factories中的EnableAutoConfiguration对应的全类名,其中XXXAutoConfiguration都是一个个自动配置类。
自动装配原理具体参考:Spring Boot系列(二):Spring Boot自动装配原理解析
二、Spring Boot的jar启动
1、Spring Boot自动装配Tomcat组件
① EmbeddedWebServerFactoryCustomizerAutoConfiguration内嵌的Web容器工厂定制器自动装配类,装配了TomcatWebServerFactoryCustomizer组件
Tomcat工厂定制器TomcatWebServerFactoryCustomizer用来设置容器的属性,把ServerProperties中的属性设置到Tomcat容器的工厂中。
ServerProperties服务的属性类:
② ServletWebServerFactoryAutoConfiguration,ServletWeb工厂自动装配类,装配了如下四个组件
- ServletWebServerFactoryCustomizer:用来定制ServletWeb服务工厂
- TomcatServletWebServerFactoryCustomizer:用来定制TomcatServletWeb服务工厂
- ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar :后置处理器
- ServletWebServerFactoryConfiguration:用来配置TomcatServletWeb服务工厂
2、SpringApplication.run启动流程
① new SpringApplication(primarySources),创建了一个SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置主配置类 我们自己写的Spring Boot的启动类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置web应用的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置容器初始化器(ApplicationContextInitializer类型的)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//把监听器设置到SpringApplication中[ApplicationListener]
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//设置主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
② SpringApplication的run方法:
主要流程:
第一:创建容器对象
第二:去META-INFO/spring.factories中获取SpringApplicationRunListener监听器(事件发布监听器)
第三:发布容器starting事件(通过spring的事件多播器)
第四:封装命令行参数
第五:准备容器环境
第六:打印Springboot的图标
第七:根据webApplicationType来创建容器
第八:准备容器上下文
第九:发布容器启动事件
第十:发布容器运行事件
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//容器对象
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//去META-INFO/spring.factories中获取SpringApplicationRunListener监听器(事件发布监听器)
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布容器starting事件(通过spring的事件多播器)
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/**
* 准备容器环境
* 1: 获取或者创建环境
* 2:把命令行参数设置到环境中
* 3:通过监听器发布环境准备事件
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印Springboot的图标
Banner printedBanner = printBanner(environment);
//创建容器根据webApplicationType来创建容器(通过反射创建)
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
/**
* 准备上下文
* 1:把环境设置到容器中
* 2: 循环调用ApplicationContextInitializer进行容器初始化工作
* 3: 发布容器上下文准备完成事件
* 4: 注册关于Springboot特性的相关单例Bean
* 5: 发布容器上下文加载完毕事件
*/
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
//运行ApplicationRunner和CommandLineRunner
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布容器启动事件
listeners.started(context);
//运行ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//出现异常调用异常分析保护类进行分析
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
//发布容器运行事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
③ org.springframework.boot.SpringApplication#refreshContext
④ org.springframework.boot.SpringApplication#refresh
⑤ org.springframework.context.support.AbstractApplicationContext#refresh
到了AbstractApplicationContext#refresh方法,之前讲过Spring IoC源码解析讲过该方法的12大步,这里就不细说,详细可以参考:Spring系列(三):Spring IoC源码解析,里面说过有一步就是onRefresh(),这个方法默认是空的,由子类根据自身需要去实现
⑥ org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
该onRefresh方法分2步
第一:super.onRefresh(); 调用父类的onRefresh()
第二:createWebServer();创建Web服务,很重要,很重要,很重要!!!
⑦ createWebServer()方法
第一:ServletContext servletContext = getServletContext(); 获取Servlet的上下文
第二:ServletWebServerFactory factory = getWebServerFactory();获取Tomcat的Web服务工厂
第三:this.webServer = factory.getWebServer(getSelfInitializer()); 创建一个Web服务器
⑧ TomcatServletWebServerFactory#getWebServer()方法,主要用于创建一个Tomcat Web容器
到此我们知道Spring Boot的启动通过Spring IoC的refresh中的的onRefresh()带动了Tomcat的启动,跟我们之前我们学Spring Mvc的时候刚好相反,Spring Mvc的是Tomcat的启动带动了Spring容器的启动;
三、普通Web工程启动
1、普通的web工程,我们找到web.xml,会发现都配置了如下的加载Spring的配置。
2、Tomcat启动的时候会调用该上下文加载的的监听器的contextInitialized方法,我们进入到该方法:
3、进入初始化Web应用上下文initWebApplicationContext方法中:
- this.context = createWebApplicationContext(servletContext);
- configureAndRefreshWebApplicationContext(cwac, servletContext);
4、进去到configureAndRefreshWebApplicationContext(cwac, servletContext)方法中:
5、进入到refresh方法实际就到了org.springframework.context.support.AbstractApplicationContext#refresh的方法
这个方法很熟悉了,Spring IoC的refresh的12大步;
四、Spring Boot启动流程图
Spring Boot系列(四):Spring Boot源码解析的更多相关文章
- spring MVC cors跨域实现源码解析
# spring MVC cors跨域实现源码解析 > 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就 ...
- spring MVC cors跨域实现源码解析 CorsConfiguration UrlBasedCorsConfigurationSource
spring MVC cors跨域实现源码解析 spring MVC cors跨域实现源码解析 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议 ...
- 老生常谈系列之Aop--Spring Aop源码解析(二)
老生常谈系列之Aop--Spring Aop源码解析(二) 前言 上一篇文章老生常谈系列之Aop--Spring Aop源码解析(一)已经介绍完Spring Aop获取advice切面增强方法的逻辑, ...
- 老生常谈系列之Aop--Spring Aop源码解析(一)
老生常谈系列之Aop--Spring Aop源码解析(一) 前言 上一篇文章老生常谈系列之Aop--Spring Aop原理浅析大概阐述了动态代理的相关知识,并且最后的图给了一个Spring Aop实 ...
- 第十四章 Executors源码解析
前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...
- ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>
本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...
- 小学徒成长系列—StringBuilder & StringBuffer关键源码解析
在前面的博文<小学徒成长系列—String关键源码解析>和<小学徒进阶系列—JVM对String的处理>中,我们讲到了关于String的常用方法以及JVM对字符串常量Strin ...
- Java 集合系列Stack详细介绍(源码解析)和使用示例
Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out). java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现 ...
- Android进阶:四、RxJava2 源码解析 1
本文适合使用过Rxjava2或者了解Rxjava2的基本用法的同学阅读 一.Rxjava是什么 Rxjava在GitHub 主页上的自我介绍是 "a library for composin ...
- 第四章 CopyOnWriteArraySet源码解析
注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...
随机推荐
- ffmpeg获取视频封面图片
ffmpeg百度云盘下载地址:https://pan.baidu.com/s/1Am_x0abBvXTrbCFPHs5e9Q 提取码:4y2r /** * ffmpeg百度云盘下载地址:https:/ ...
- 撸了一个 Feign 增强包
前言 最近准备将公司的一个核心业务系统用 Java 进行重构,大半年没写 Java ,JDK 都更新到 14 了,考虑到稳定性等问题最终还是选择的 JDK11. 在整体架构选型时,由于是一个全新的系统 ...
- PHP zip_entry_read() 函数
定义和用法 zip_entry_read() 函数从打开的 zip 档案中获取内容.高佣联盟 www.cgewang.com 如果成功,该函数则返回项目的内容.如果失败,则返回 FALSE. 语法 z ...
- luogu P6097 子集卷积 FST FWT
LINK:子集卷积 学了1h多 终于看懂是怎么回事了(题解写的不太清楚 翻了好几篇博客才懂 一个需要用到的性质 二进制位为1个数是i的二进制数s 任意两个没有子集关系.挺显然. 而FST就是利用这个性 ...
- 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径
LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...
- 4.26 省选模拟赛 T3 状压dp 差分求答案
LINK:T3 比较好的题目 考试的时候被毒瘤的T2给搞的心态爆炸 这道题连正解的思路都没有想到. 一看到题求删除点的最少个 可以使得不连通. 瞬间想到最小割 发现对于10分直接跑最小割即可. 不过想 ...
- 洛谷3月月赛div2 题解(模拟+数学+贪心+数学)
由于本人太蒻了,div1的没有参加,胡乱写了写div2的代码就赶过来了. T1 苏联人 题目背景 题目名称是吸引你点进来的. 这是一道正常的题,和苏联没有任何关系. 题目描述 你在打 EE Round ...
- 求解最长递增子序列(LIS) | 动态规划(DP)+ 二分法
1.题目描述 给定数组arr,返回arr的最长递增子序列. 2.举例 arr={2,1,5,3,6,4,8,9,7},返回的最长递增子序列为{1,3,4,8,9}. 3.解答 ...
- 您能解决这3个(看似)简单的Python问题吗?
尝试解决以下问题,然后检查以下答案. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识 ...
- 14、Java文件操作stream、File、IO
1.文件操作涉及到的基本概念 File File类 是文件操作的主要对象中文意义就是 文件 顾名思意 万物皆文件,在计算上看到的所有东西都是文件保存,不管是你的图片.视频.数据库数据等等都是按照基本的 ...