springboot启动过程_spring5/springboot2源码学习 -- spring boot 应用的启动过程

推荐阅读:

  • Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC
  • 疫情期间“闭关修炼”,吃透这本Java核心知识,跳槽面试不心慌
  • 2020“闭关”跳槽季,啃透分布式三大技术:限流、缓存、通讯

基本环境

开发工具:Intellij IDEA 2017(盗)版

java版本:1.8.0_151

spring的github地址:spring-framework

准备:git clone或直接下载github上的spring源码,导入idea中,在项目路径下执行gradle build (如果本机没有gradle环境,或者版本差很多,就用gradlew代替),会build很久,可以事先将阿里的maven仓库地址加到repositories中,像这样:

repositories {  maven {            url "http://maven.aliyun.com/nexus/content/groups/public/"        }        maven { url "https://repo.spring.io/plugins-release" }}

会用到的缩写:

ApplicationContext -> ACApplicationContextInitializer -> ACIBeanFactory -> BFApplicationListener -> ALEnvironmentPostProcessor -> EPPspring boot 应用的启动/*** @author pk* @date 2018/02/22*/@SpringBootApplicationpublic class SpringBootTwoStudyApplication {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(SpringBootTwoStudyApplication.class);//可以在run之前,对AC进行一些自定义的配置,添加点ApplicationListener,ApplicationContextInitializer啥的springApplication.run(args);}}
832e787a2f3040fbd4a092e3e1c248f4.png

一.SpringApplication的初始化

代码如下:

public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {  this.resourceLoader = resourceLoader;        Assert.notNull(primarySources, "PrimarySources must not be null");        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//1        this.webApplicationType = deduceWebApplicationType();//2        setInitializers((Collection) getSpringFactoriesInstances(                ApplicationContextInitializer.class));//3        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//4        this.mainApplicationClass = deduceMainApplicationClass();//5    }

1.设置启动类primarySources到对应属性

这个启动类就是在初始化SpringApplication时候的参数,可以有多个

2.获取web应用类型

  • 根据当前classpath下存在的类来判断的:
  • 如果存在org.springframework.web.reactive.DispatcherHandler,就是REACTIVE
  • 如果不存在org.springframework.web.servlet.DispatcherServlet或者javax.servlet.Servlet,就是NONE
  • 否则,就是SERVLET

3.设置ApplicationContextInitializer

代码如下:

private  Collection getSpringFactoriesInstances(Class type,                                                      Class>[] parameterTypes, Object... args) {        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//3.1        Set names = new LinkedHashSet<>(                SpringFactoriesLoader.loadFactoryNames(type, classLoader));//3.2        List instances = createSpringFactoriesInstances(type, parameterTypes,                classLoader, args, names);//3.3        AnnotationAwareOrderComparator.sort(instances);//3.4        return instances;    }

3.1 获取线程上下文ClassLoader

TODO(ClassLoader的相关解释)

3.2 使用SpringFactoriesLoader加载出classpath下所有路径为META-INF/spring.factories的资源文件,并读取key为ApplicationContextInitializer的值

会得到如些几个类(实际是全称类名):

SharedMetadataReaderFactoryContextInitializerAutoConfigurationReportLoggingInitializerConfigurationWarningsACIContextIdACIDelegatingACIServerPortInfoACI

3.3 利用反射创建出上述ACI的实例

3.4 排序

根据Ordered接口/@PriorityOrder注解/@Order注解去排

其他地方的排序基本也是按照这个规则来排的

4.设置listener

跟设置ACI的方法一样,也是读取spring.factories文件中的对应key项所对应的所有类名,然后实例化、排序.

得到如下几个ApplicationListener:

BackgroundPreinitializerClearCachesALParentContextCloserALFileEncodingALAnsiOutputALConfigFileALDelegatingALClasspathLoggingALLoggingALLiquibaseServiceLocatorAL

5.设置mainApplicationClass

就是找到main方法所在的类.是唯一的,跟上面的primarySources不是一回事

二.SpringApplication的run(String … args)方法

代码如下:

public ConfigurableApplicationContext run(String... args) {  StopWatch stopWatch = new StopWatch();        stopWatch.start();//1        ConfigurableApplicationContext context = null;        Collection exceptionReporters = new ArrayList<>();        configureHeadlessProperty();//2        SpringApplicationRunListeners listeners = getRunListeners(args);//3        listeners.starting();//4        try {            ApplicationArguments applicationArguments = new DefaultApplicationArguments(                    args);//5            ConfigurableEnvironment environment = prepareEnvironment(listeners,                    applicationArguments);//6            configureIgnoreBeanInfo(environment);//7            Banner printedBanner = printBanner(environment);//8            context = createApplicationContext();//9            exceptionReporters = getSpringFactoriesInstances(                    SpringBootExceptionReporter.class,                    new Class[] { ConfigurableApplicationContext.class }, context);//10            prepareContext(context, environment, listeners, applicationArguments,                    printedBanner);//11            refreshContext(context);//12            afterRefresh(context, applicationArguments);//13            listeners.finished(context, null);//14            stopWatch.stop();            //15            if (this.logStartupInfo) {                new StartupInfoLogger(this.mainApplicationClass)                        .logStarted(getApplicationLog(), stopWatch);            }            callRunners(context, applicationArguments);//16            return context;        }        catch (Throwable ex) {            handleRunFailure(context, listeners, exceptionReporters, ex);            throw new IllegalStateException(ex);        }    }

1. 创建一个StopWatch用于记录启动的时间

2. 配置java.awt.headless属性,默认为true

这个headless是一种模式,是指缺少显示屏、键盘或者鼠标时的系统配置

我找了spring和spring boot的源码,只有ImageBanner里面用到了

3.从spring.factories中加载出SpringApplicationRunListener,并构建一个SpringApplicationRunListeners

  • StringApplicationListeners就是对对个SpringApplicationRunListener的包装
  • 现在看只有一个具体实现类
  • EventPublishingRunListener
  • 虽然这个类叫做listener,但其实起的作用是传播事件,更像是一个ApplicationEventMulticaster。
  • spring boot启动过程中的事件,主要由这个类来传播

4.发布一个ApplicationStartingEvent事件

  • 关于spring事件发布机制,请看spring事件机制
  • 响应的AL
  • LoggingAL:初始化日志系统
  • 做法是按照logback>log4j>jul的顺序查找classpath下存在的相关类
  • LiquibaseServiceLocatorAL:实在是没有用过这个东西,不知道这个LiquibaseService是个啥…

5.根据启动时的命令行参数构建一个ApplicationArguments

ApplicationArguments的接口定义:

public interface ApplicationArguments {  String[] getSourceArgs();    //--开头的参数    Set getOptionNames();    boolean containsOption(String name);    //--开头的参数是允许重复定义的,不会覆盖      List getOptionValues(String name);    List getNonOptionArgs();}

这个类的作用就是解析所有的启动参数的

6.prepareEnvironment():创建并配置environment

代码如下:

private ConfigurableEnvironment prepareEnvironment(  SpringApplicationRunListeners listeners,            ApplicationArguments applicationArguments) {        ConfigurableEnvironment environment = getOrCreateEnvironment();//6.1        configureEnvironment(environment, applicationArguments.getSourceArgs());//6.2        listeners.environmentPrepared(environment);//6.3        bindToSpringApplication(environment);//6.4        if (this.webApplicationType == WebApplicationType.NONE) {            environment = new EnvironmentConverter(getClassLoader())                    .convertToStandardEnvironmentIfNecessary(environment);        }        ConfigurationPropertySources.attach(environment);//6.5        return environment;    }

6.1 根据WebApplicationType创建对应类型的environment

关于spring中Environment/PropertySource的介绍:

根据webApplicationType:

SERVLET -> StandardServletEnvironmentNONE/REACTIVE -> StandardEnvironment

初始化的时候会在构造函数中调用customizePropertySources()方法,其结果是会在environment的propertySources属性的propertySourcesList列表中加入以下PropertySource:

  • 代表ServletConfig的构建参数的,名为servletConfigInitParams的StubPropertySource(目前只是作为占位符存在)
  • 代表ServletContext的构建参数的,名为servletContextInitParams的StubPropertySource(目前只是作为占位符存在)
  • 代表jvm属性参数的名为systemProperties的MapPropertySource(来源于System.getProperties()方法)
  • 代表环境变量的名为systemEnvironment的SystemEnvironmentPropertySource(来源于System.getEnv()方法)

6.2 配置environment

配置PropertySources

  • 看看SpringApplication的defaultProperties属性是否为空,如果不为空则加入一个MapPropertySource到propertySources中。这个defaultProperties属性可以在SpringApplication调用run方法之前通过setDefaultProperties()方法设置
  • 看看有没有命令号参数,如果有则创建一个SimpleCommandLinePropertySource加入到propertySources中

配置activeProfiles:就是将spring.profiles.active属性的值作为数组读入

6.3 listeners发布一个ApplicationEnvironmentPreparedEvent

这是一个很重要的事件,响应的listener有很多:

ConfigFileApplicationListener

  • 从spring.factories中读取所有的EnvironmentPostProcessor,它自己也是个EPP。
  • 排序,然后逐个调用
  • SystemEnvironmentPropertySourceEPP:替换systemEnvironment为其内部类OriginAwareSystemEnvironmentPropertySource
  • SpringApplicationJsonEPP:解析spring.application.json属性,作为JsonPropertySource加入propertySources中

CloudFoundryVcapEPP:VCAP相关,不懂,略过

ConfigFileAL(自己):

  • 添加一个RandomValuePropertySource,作用是设置所有random.int/long开头的属性为一个随机值
  • 从spring.factories中读取PropertySourceLoader:
  • PropertiesPropertySourceLoader:读取.properties配置文件
  • YamlPropertySourceLoader:读取.yaml配置文件
  • 根据activeProfiles解析每个对应的配置文件成OriginTrackedMapPropertySource,加入到propertySources中

6.4 将environment绑定到SpringApplication中

todo…

6.5 往propertySources中加入一个ConfigurationPropertySourcesPropertySource

7.配置一个spring.beaninfo.ignore属性,默认为true

8.打印banner

9.根据webApplicationType构建ApplicationContext

SERVLET -> AnnotationConfigServletWebServerACREACTIVE -> ReactiveWebServerACNONE -> AnnotationConfigAC 

按照继承结构:

DefaultResourceLoader:ResourceLoader的默认实现,作用就是加载Resource,内部有个比较重要的ProtocolResolver集合属性,具体使用请看ProtocolResolver解析

AbstractAC:配置了一个ResourcePatternResolver,具体请看Resource相关

GenericAC:初始化了一个BeanFactory,具体类型是DefaultListableBeanFactory

作者:pumpkin_pk

原文链接:https://blog.csdn.net/yuxiuzhiai/article/details/79074249

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python concat去除重复值语句_Python数据处理从零开始----第二章(pandas)④数据合并和处理重复值...

目录第二章(pandas)Python数据处理从零开始----第二章(pandas)④数据合并和处理重复值数据合并在数据处理中&#xff0c;通常将原始数据分开几个部分进行处理而得到相似结构的Series或DataFrame对象&#xff0c;我们该如何进行纵向合并它们&#xff1f;这时我们可以选择用pd.co…

[html] noscript标签有什么作用?

[html] noscript标签有什么作用&#xff1f; noscript 标签用于当浏览器不支持 JS 的时候在页面上显示一些提示内容&#xff0c;但是也有一些缺点&#xff0c;比如如果是防火墙而不是浏览器禁用了 JS&#xff0c;非但 JS 执行不了&#xff0c;noscript 的内容也不会显示。比较…

ntp时间同步

一、NTP服务器就是利用NTP协议提供时间同步服务的 二、安装 # 系统自带ntp [rootoldboyedu ~]# rpm -qa ntp ntp-4.2.6p5-5.el6.centos.x86_64 # 如果没有就安装 yum -y install ntp三、配置ntp [rootVM-0-14 ~]# vim /etc/ntp.conf # restrict default kod nomodify notrap no…

不出来信号 quartus_男人一旦动了真情,会向你发出这6个“信号”不爱的人装不出来...

恋爱的时候&#xff0c;女人都想知道一个男人是否真的爱你&#xff0c;是否真的在乎你&#xff0c;女人常常在猜测男人的心思。而对一个聪明的女人来说&#xff0c;她们会用眼睛&#xff0c;用心观察身边的那个男人。有人说如果男人对女人动了真情&#xff0c;他会在女人面前表…

python turtle画烟花_用Python写一个绚丽的烟花!

Python人工智能 - 一节课快速认识人工智能必备语言:python - 创客学院直播室​www.makeru.com.cn 哈喽大家好&#xff0c;小编来教大家如何用Python写一个绚丽的烟花&#xff0c;下面我们开始吧~ Turtle库Turtle&#xff0c;也叫海龟渲染器&#xff0c;使用Turtle库画图也叫海龟…

zabbix详解

官网地址 https://www.zabbix.com/documentation/3.0/manual/config/items/itemtypes/zabbix_agent 使用率 转载于:https://www.cnblogs.com/zhaojingyu/p/11440579.html

python设置一个初始为0的计数器_python中统计计数的几种方法

以下实例展示了 count() 方法的使用方法&#xff1a;以上实例输出结果如下&#xff1a;1) 使用字典dict()循环遍历出一个可迭代对象中的元素,如果字典没有该元素,那么就让该元素作为字典的键,并将该键赋值为1,如果存在就将该元素对应的值加1.2) 使用defaultdict()defaultdict(p…

怎么把整个网站的代码中的一个词去掉_【杭州南牛网络】网站优化的最新优化方法...

【杭州南牛网络】如果你是一名企业主&#xff0c;你有建立企业官方网站的经验&#xff0c;在2-3年的运营过程中&#xff0c;我相信你至少对网站做了一次修改&#xff0c;甚至对SEO战略进行了重大调整。 原因很简单&#xff1a;当我们刚开始建一家公司时&#xff0c;很多时间都是…

[html]说说页面中字体渲染规则是怎样的?会有哪些因素影响字体的渲染?

[html]说说页面中字体渲染规则是怎样的&#xff1f;会有哪些因素影响字体的渲染&#xff1f; 字体渲染&#xff1a; 1.解码&#xff0c;根据web服务器返回的&#xff08;或者本地网页本身的&#xff09;content-type charset等信息确定编码&#xff0c;将网页解码成Unicode字符…

数字电影打包内容(Packaging)

在数字电影领域&#xff0c;打包&#xff08;Packaging&#xff09;是指将节目素材&#xff08;含图像、声音和字幕&#xff09;按照一定的规则进行封装和组织&#xff0c;以用于节目传输和存储。在数字电影发展初期&#xff0c;下发到影院的数字节目没有统一的播放格式&#x…

阿里云盾AliYunDun服务IO超高

停止阿里云盾AliYunDun服务解决大量写磁盘问题&#xff0d;小内存ECS服务器 阿里云数据库在没备案&#xff0c;涉及大量IO操作时会自动启动阿里云盾这个服务&#xff0c;会导致服务器变得很卡&#xff0c;一直持续百分之99&#xff0c;一顿重启没有什么卵用&#xff01; 最后关…

js页面离开提示

window.onbeforeunload function() { if (blnCheckUnload) return ("确认退出&#xff1f;");}转载于:https://www.cnblogs.com/sususu/archive/2012/03/15/2397733.html

CORS 请求未能成功_当遇到跨域开发时, 我们如何处理好前后端配置和请求库封装(koa/axios版)...

我们知道很多大型项目都或多或少的采用跨域的模式开发, 以达到服务和资源的解耦和高效利用. 在大前端盛行的今天更为如此, 前端工程师可以通过nodejs或者Nginx轻松搭建起web服务器.这个时候我们只需要请求后端服务器的接口即可实现系统的业务功能开发.这个过程中会涉及到web页面…

大屏某区域滚动效果循环

// 数据自动滚动事件BEGIN var botTimer null; // 定时器对象 $(.tbody).hover(function () { clearInterval(botTimer); // hover}, function () { scrollCore(tbodyScroll, tbody); //out}); //function scrollCore(outerId, innerId) { var winHei $("#" oute…

vue-cli部署ngixs_Vue-cli项目部署到Nginx

项目环境&#xff1a;0. Nginx使用以windows版为例&#xff0c;下载niginx压缩包并解压到任意目录&#xff0c;双击nginx.exe&#xff0c;在浏览器中访问http://localhost&#xff0c;如果出现Welcome to nginx!页面则说明成功。nginx常用命令如下&#xff1a;nginx -h# 打开帮…

2.9.JavaScript--内置对象

<!DOCTYPE HTML><html lang"en-US"><head><meta charset"UTF-8"><title>内置对象</title></head><body><script type"text/javascript">/*var email prompt("请输入电子邮箱地址&q…

java 数组存入数据库_Java中关于二维数组的理解与使用

今天练习的时候遇到一个题目&#xff0c;题目需求是这样的&#xff1a;需求说明&#xff1a;根据3个班各5名学生某门课程的成绩&#xff0c;正确使用二维数组计算如图所示3个班各自的总成绩分析&#xff1a;要实现这个功能需要存储两个信息&#xff1a;一个是班级信息&#xff…

feign接口调用出现连接超时

确认代码无误后,调用出现 connect time out 问题. 因为feign已经集成robbon,hystrix,调用在规定时间内达不到就会报上述错误,并且这个规定时间会很短 解决办法:配置文件加入如下配置: 指的是在调用的一方 #hystrix的超时时间hystrix:command:default:execution:timeout:ena…

mysql截取字符串最后两位_Mysql字符串截取函数SUBSTRING的用法说明

感觉上MySQL的字符串函数截取字符&#xff0c;比用程序截取(如PHP或JAVA)来得强大&#xff0c;所以在这里做一个记录&#xff0c;希望对大家有用。函数&#xff1a;1、从左开始截取字符串left(str, length)说明&#xff1a;left(被截取字段&#xff0c;截取长度)例&#xff1a;…

sql删除元组_Lecture #02: 中级SQL

本专栏是对CMU15445的笔记的翻译&#xff0c;原文地址&#xff1a;https://15445.courses.cs.cmu.edu/fall2020/notes/02-advancedsql.pdf1 关系语言在20世纪70年代&#xff0c;Edgar Codd发布了一篇关于关系模型的论文。最初&#xff0c;他只定义了数据库在关系模型上运行查询…