【源码分析】Springboot启动流程源码分析

news2024/9/21 12:39:28

1.简介

springboot版本:2.7.2

SpringApplication类是用来执行Spring框架启动的引导类。
有两种方式可以进行启动引导:

  • 通过静态方法SpringApplication.run启动。
  • 先创建SpringApplication实例,在调用的实例方法run进行启动。
    无论是以上哪种方式,最终都是通过创建SpringApplication实例,在调用run()启动。
    public static void main(String[] args) {
        SpringApplication.run(StateMachineApplication.class, args);
    }

2.源码总结分析

类:org.springframework.boot.SpringApplication

run(String… args) 方法分析

主要方法:下面

 public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        //初始化启动上下文
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        //配置Headless模式:Headless模式是在环境缺少显示器等设备情况下的一种配置,和启动流程并无太多关系
        this.configureHeadlessProperty();
        //listeners 监听器可以用来监听SpringApplication启动过程中的各个阶段。
        //默认的监听器是EventPublishRunListener,用户也可以通过实现SpringApplicationRunListener接口,实现应用程序对SpringApplication启动过程的监听。
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
        //准备环境,创建ConfigurableEnvironment对象
        //SpringApplication会创建Spring启动所需的环境,这个环境主要由ConfigurableEnviroment对象表示。首先,该对象确认了程序是否需要设置Web环境,其次,该对象还确定了程序所需要的参数和读取的配置文件等信息。此步骤会回调SpringApplicationRunListener的environmentPrepared()方法,通知监听器环境已经准备好。
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            //决定是否跳过 BeanInfo 类的扫描
            this.configureIgnoreBeanInfo(environment);
            //只是会向控制台或是日志中输出Spring的Logo和版本信息
            Banner printedBanner = this.printBanner(environment);
            //创建应用程序上下文并加载Bean
            //扩展:在准备好环境之后,接下来要做的就是创建应用程序上下文ApplicationContext对象。ApplicationContext是Spring IoC的核心组件,它不仅是程序所需Bean的容器,还提供了国际化,事件发布等功能。
            //在创建应用程序上下文的时候,首先会根据之前配置决定上下文的具体类型(AnnotationConfigApplicationContext或是AnnotationConfigServletWebServerApplicationContext)。再通过反射实例化到对象。
            context = this.createApplicationContext();
            //为此应用程序上下文设置ApplicationStartup。
            context.setApplicationStartup(this.applicationStartup);
            //准备ApplicationContext
            //扩展:
            //虽然已经得到了ApplicationContext对象,但此时的对象还只是一个空白对象,需要准备和处理后,ApplicationContext才能被使用。
            //在准备过程中主要做了做了几件事:为ApplicationContext设置之前准备好的Environment对象。
			//通过对ApplicationContext后置处理或是BeanDefinitionLoader等方式往容器中添加一些初始的Bean。
			//应用默认的初始化器初始化应用程序上下文(责任链模式的应用,多个初始化器形成一个List,应用程序需要被每个初始化器应用一次,每个初始化器有自己的职责)。
			//准备过程中ApplicationRunListener发出两个消息,分别是contextPrepared和contextLoaded。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            //刷新上下文
            //扩展:(下面在去解析这个方法,比较复杂)
            //通过刷新应用程序上下文发现Bean并加载到容器中。refreshContext()会调用ApplicationContext.refresh()方法。
			//AbstractApplicationContext中定义了refresh()方法的基本框架(模板模式的应用)。
            this.refreshContext(context);
            //在Application完成刷新后,SpringApplication给子类留了afterRefresh()的方法作为回调。
            this.afterRefresh(context, applicationArguments);
            //启动完成后,stopWatch会记录下本次启动消费的时间。
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
            //然后向ApplicationRunListener发布started事件,说明已经启动就绪
            listeners.started(context, timeTakenToStartup);
            //启动完成后,正式运行前,SpringApplication还会执行用户定义的ApplicationRunner和CommandLineRunner两个接口中定义的run()方法。
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            //在执行完成后,向ApplicationRunListener发布runing的消息。至此,启动流程结束。
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

this.refreshContext(context);分析

类:org\springframework\context\support\AbstractApplicationContext.java

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			//准备刷新
			//准备刷新的阶段做了初始化和校验的工作。
			prepareRefresh();

			// 告诉子类刷新内部bean工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.(准备用于此上下文的bean工厂。)
			//准备BeanFactory  
			//扩展:
			//对BeanFactory的准备主要是:添加一些必要组件,比如类加载器,表达式解析器,属性编辑器注册表等。
			//以及一些后置处理器,比如ApplicationContextAwareProcessor(xxxAware的接口就是通过后置处理器在Bean创建的时候,通过后置处理器设置的)。此外还有一些特殊的Bean,environment,systemProperties和systemEnvirnoment。
			prepareBeanFactory(beanFactory);

			try {
				// (后置处理BeanFactory)Allows post-processing of the bean factory in context subclasses.
				//扩展:
				//对于非WebServlet环境的ApplicationContext而言这个方法是个空方法,但是Web环境下的ApplicationContext会通过这个方法定制一些后处理动作,比如添加WebApplicationContextServletAwareProcessor后置处理器,添加在web环境中可能使用的Scope(session和request)。
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				//(在上下文中调用注册为bean的工厂处理器。) Invoke factory processors registered as beans in the context.
				//其实就是实例化并调用BeanFactoryPostProcessor
				//扩展:(看标题有详细解释)
				//BeanFactoryPostProcessor是一种特殊的后置处理器,其操作的对象是针对BeanFactory。
				invokeBeanFactoryPostProcessors(beanFactory);

				// (注册Bean后置处理器)Register bean processors that intercept bean creation.\
				//扩展:
				//上一步是针对BeanFactory和BeanDefinitionRegistry的后置处理器,这一步从BeanFactory中获取针对普通Bean的后置处理器BeanFactoryPostProcessor放到专门的容器beanPostProcessors中。
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				//(初始化MessageSource处理国际化相关内容) Initialize message source for this context.
				initMessageSource();

				//(初始化ApplicationEventMulticaster) Initialize event multicaster for this context.
				//扩展:
				//ApplicationEventMulticaster是ApplicationEvent广播器,可以通过这个对象向容器中添加移除Listener,也可以通过这个对象发布事件(观察者模式的应用)。
				initApplicationEventMulticaster();

				// (刷新应用程序)Initialize other special beans in specific context subclasses.
				// 扩展:
				//发现了所有的Bean,并且需要实例化的Bean也都被创建好了之后,Spring接下去要做的是创建ThemeSource(和主题相关的组件),以及创建Webserver如果存在。
				//此方法可以重写以添加特定于上下文的刷新工作的模板方法。在实例化单体之前,在初始化特殊bean时调用。
				onRefresh();

				//注册监听器 Check for listener beans and register them.
				//扩展:
				//这一步会将初始化得到的ApplicationListener方法和容器中获得ApplicationListener一起注册到ApplicationEventMulticaster中,并且如果存在需要早起发布的事件,则发布事件。
				registerListeners();

				// (初始化容器中的Bean)Instantiate all remaining (non-lazy-init) singletons.
				//扩展:
				//经过之前的步骤,现在容器中必要的组件都已经准备好了,并且所有需要容器管理的Bean也都已经被发现注册成BeanDefinition注册表中。
				//对于Scope是Singleton的Bean而言,此时已经具备了实例化Bean的条件,因此在这一步中,Spring会对所有Singleton且非lazy-init的Bean进行实例化。
				//主要做法就是获取容器中所有为singletion且非lazyInit的BeanDefinition,然后通过getBean创建出Bean的实例,保存在容器内部。
				finishBeanFactoryInitialization(beanFactory);

				//完成刷新 Last step: publish corresponding event.
				//主要是一些资源清理以及注册LifeCycleProcessor。LifeCycleProcessor可以用来在 Spring 生命周期的refresh和close时触发回调。并且发布Refresh的消息。ContextRefreshedEvent
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				//销毁已创建的单实例以避免悬空资源。Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// 重置“激活”标志。 Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				//清除缓存
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

BeanFactory 详解

BeanFactory是 Spring 框架中容器的底层实现,所有的 Bean 都存放在BeanFactory中,虽然ApplicationContext也实现了BeanFactory接口,但是在其内部还是将获取 Bean 的相关操作委托给内部的DefaultListableBeanFactory变量,只是ApplicationContext帮用户屏蔽了底层的操作,同时提供出一些更符合外部用户使用的接口。

BeanFactoryPostProcessor 详解

BeanFactoryPostProcessor 主要有三个后置处理器,分是:

1SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor

2ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor

3ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

一般主要是这俩:
在这里插入图片描述
我们看名字也能发现类是怎么来的(外部类是xxxInitializer的就说明是初始化器设置的)。

其中第一个类和启动流程有关,因为它会向容器注册ConfigurationClassPostProcessor。

如果BeanFactoryPostProcessor同时又是BeanDefinitionRegistryPostProcessor,则先进行针对BeanDefinition注册表的后置处理,目的是为了发现Bean。

在最初的三个BeanFactoryProcessor后置处理完成后,会从容器中获取BeanDefinitionRegistryPostProcessor类型的后置处理器(这里主要会得到刚才加载的ConfigurationClassPostProcessor实例)。再调用这些BeanDefinitionRegistry的后置处理器,继续向发现并向容器中注册新的Bean。

这里主要是通过@Configuration注解作为入口发现Bean,如果发现的Bean中又存在新的@ConfigurationBean,则以此Bean为入口再进行发现,直到所有的Bean都被发现。

注意 Bean的发现过程只是向BeanDefinition注册表注册BeanDefinition的过程,并没有针对发现的Bean进行实例化(少部分需要用到的Bean会进行实例化,比如这部分会对BeanDefinitionRegistryPostProcessor类型的Bean实例化)。

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

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

相关文章

资源管理管理

资源管理目录概述需求:设计思路实现思路分析1.PMI hr的理念2.双因素的理论3.马斯洛的需求层析理论4.延伸5.X,Y理论Z 理论期望理论:亚当斯公共理论边际福利,光环效应,额外待遇,皮的原理,KISS原理…

阿里P9大牛带你在简历上写精通Spring与Boot高级功能

前言 蓦然回首自己做开发已经十年了,这十年中我获得了很多,技术能力、培训、出国、大公司的经历,还有很多很好的朋友。但再仔细一想,这十年中我至少浪费了五年时间,这五年可以足够让自己成长为一个优秀的程序员&#…

QT - 模型与视图

经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。 MVC 不是一…

银行 测试|测试开发 面试真题|面经 汇总

招银测开面经 一面 1.面向对象3大特征 2.java是否能多继承,怎么实现多继承 3.抽象类 4.数据库特性,隔离级别 5.sql语句查找 6.static关键字的使用 7.手撕:寻找字符串出现频率最高的字符 8.代码改进和测试 二面 1.先闲聊了几句&…

Python面向对象编程

本篇内容: 1、反射  2、面向对象编程  3、面向对象三大特性  4、类成员  5、类成员修饰符  6、类的特殊成员  7、单例模式 反射 python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象…

m基于GA遗传算法的高载能负荷响应优化控制模型matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 高载能企业执行子站接收负荷调整指令后,需将有功功率、无功功率调整总量合理分配给各用能设备/系统。研究高载能负荷响应优化控制模型,建立以高载能企业响应效益最优为目标…

ccf序列查询新解python满分_纯数学规律(学霸怎么想到的啊......)

题目 思路和代码 这题我也就看了好几个小时吧。终于!有点懂了! 上午看懂了用双指针写《下一个排序》后就在看这题。70分的代码很好写也很好想,就是模拟fx和gx,然后遍历一趟,得到最终的结果。 看了学霸的代码&#x…

使用桥梁振动自动识别车辆(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

Qt在线安装教程(详细图文)

Qt在线安装教程(详细图文一、前言二、QT账号的注册三、QT的安装的镜像四、安装的过程一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡,小伙伴们,让我们一起来学习Qt在线安装。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连) 二、QT账号…

js-mark新时代的网页标记容器

js-mark 🖍️️ ✨ 它提供了一组可交互操作的工具来注释网页内容 ✨🖍️ js-mark是一个JavaScript库,用于在浏览器。他是一个可以在任何网页做标记的前端库, 它提供了一组可交互操作的工具来注释网页内容。 支持标记文本和 持久化存储与还原…

LSTM和双向LSTM讲解及实践

目录: RNN的长期依赖问题LSTM原理讲解双向LSTM原理讲解keras实现LSTM和双向LSTM RNN 的长期依赖问题 在上篇文章中介绍的循环神经网络RNN在训练的过程中会有长期依赖的问题,这是由于RNN模型在训练时会遇到梯度消失(大部分情况)或者梯度爆炸(很少&…

网络1323的分类行为

( A, B )---2*30*2---( 1, 0 )( 0, 1 ) 用网络分类A和B,让A是(0,1)(1,1),让B是(1,0)(1,1)。测试集为&#xf…

[附源码]计算机毕业设计springboot学生在线考试系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

408 考研《操作系统》第一章第三节:中断和异常、系统调用

文章目录教程1.中断和异常1.1 中断的作用1.2 中断的类型1.2.1 外中断的处理过程1.2.1 内中断的处理过程1.3 中断机制的基本原理1.4 总结2. 系统调用2.1 什么是系统调用?2.2 小例子:为什么系统调用是必须的?2.3 什么功能要用系统调用实现&…

m基于随机接入代价的异构网络速率分配算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 无线接入技术发展迅速,异构网络并存的现象普遍存在;同时,随着终端用户数量的剧增、业务类型的多样化和高服务质量多媒体数据业务需求的增加,通过异…

磨金石教育插画技能干货分享|插画怎么配色才好看?

在绘画界,配色是非常重要的步骤,一幅作品能够展现出什么样的品质,配色起着举足轻重的作用。颜色配的好会给作品晋商添花,配的不好,就会让作品失去水准,缺少神韵。 所以想学好插画,就要在在配色上…

python离线环境下安装第三方模块的方法

一.背景 1.背景: 在实际开发中,我们自己电脑上方便上网可以随时安装自己需要的包文件,但是有的项目现场不能联网或者现场是“内网”不具备联网条件,所以必须解决在“离线电脑上”安装需要的软件包的问题。 2.环境说明以及实现步…

SpringCloud微服务项目实战 - 项目搭建

面对大河我无限惭愧 我年华虚度空有一身疲倦 系列文章目录 项目搭建app登录 一、项目介绍 1. 项目背景 项目概述: 类似于新闻头条,是一个新闻资讯类项目 (这里之后放项目APP端的截图) 技术架构: 项目术语&…

Ubuntu18.04安装ROS、Gazebo、Mavros、PX4、QGC教程

修改国内源 修改apt sudo cp /etc/apt/source.list /etc/apt/source.list.old sudo gedit /etc/apt/source.list输入如下进行保存 deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic m…