springboot启动过程原理分析

news2025/1/13 10:41:15

前言

现在绝大多数java项目都上了Springboot框架, 因此深入理解Springboot框架的运行原理,能帮助我们更好的在Springboot框架下进行业务开发,同时能学习框架中优秀的设计思想, 本文主要是通过对Springboot源码的分析, 来理解整个springboot项目的启动流程. 因为Springboot不同版本的源码有差异, 因此特别声明, 本文是基于2.2.10.RELEASE版本进行的原理分析.

1. 导入依赖

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.10.RELEASE</version>
    </parent>

1.1 Springboot项目在spring官网已经作为顶级项目存在, 官网描述非常清楚,如果只是想搭建一个springboot项目, 只需要导入spring-boot-starter-parent, 如果是做web开发, 则还需导入spring-boot-starter-web, 本文只分析Springboot的启动流程,因此只需要导入spring-boot-starter-parent

2. 创建启动类

@SpringBootApplication
public class SpringbootApplication{
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

2.1 一个Springboot项目只需要在启动类上加@SpringBootApplicatio注解就可以, 可能很多人会有疑问,为什么加入了这个注解,就是一个springboot项目了,回答这个问题之前,我们首先要知道什么是一个Springboot项目, 接下来我会从源码角度为大家深入分析.

3. springboot启动流程图

在这里插入图片描述

3.1 以上是整个Springboot的run方法的执行流程, 这只是一个总体流程图, 接下里对每一步我将通过源码深入分析.

4. 实例化SpringAppliaciton

4.1 实例化SpringAppliaciton的核心是实例化了两种类型的class, 一个是ApplicationContextInitializer.class, 另一个是 ApplicationListener.class

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

4.2 getSpringFactoriesInstances该方法的核心是实例化所有依赖下META-INF/spring.factories文件里ApplicationContextInitializer和ApplicationListener类型, 并放入initializers和listeners属性中

//将META-INF/spring.factories目录下对应类型的全类名加入到set集合中
Set<String> names = new LinkedHashSet<>
(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

//通过反射实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

4.3 SpringFactoriesLoader.loadFactoryNames方法就是去加载META-INF/spring.factories, 请对这个方法有些印象, 因为这个方法在Springboot自动装配的时候也会调用.

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
 		//对应类型的全类名,这里是org.springframework.context.ApplicationContextInitializer和org.springframework.context.ApplicationListener
        String factoryTypeName = factoryType.getName(); 
        
       //通过key筛选对应的value集合
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	//这个集合在web项目的源码中经常出现,它和普通的map区别在于一个key可以对应多个value
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
        	//核心是加载所有类路径下META-INF/spring.factories文件
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

4.4 createSpringFactoriesInstances是通过反射实例化所有的对象

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
		ClassLoader classLoader, Object[] args, Set<String> names) {
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

4.5 因为是加载所有类路径下的spring.factories文件, 因此会加载spring-boot-starter-parent依赖的所有模块的spring.factories文件, 默认是加载8个initializer对象和12个listener对象.

5. 配置headless模式

5.1 headless模式springboot是默认开启的,通过System.setProperty(“java.awt.headless”,”true”)设置, 因为对于服务端而言,可能缺少显示屏、键盘或者鼠标等设备.

private void configureHeadlessProperty() {
	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
			System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

6. 获取所有的SpringApplicationRunListeners

6.1 同样是加载META-INF/spring.factories文件下所有的SpringApplicationRunListener类型的全类名,通过反射实例化.
因为我们只导入了spring-boot-starter-parent包,在子模块spring-boot下的META-INF/spring.factories文件中只有一个EventPublishingRunListener实现了SpringApplicationRunListener,因此只实例化一个SpringApplicationRunListener类型

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

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

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

相关文章

Spring:Spring框架_事务传播和隔离级别 ④

一、事务和四大特性 事务&#xff1a;指数据库的一个执行操作单元 事务的四个特性&#xff08;ACID&#xff09; 1.1 原子性 原子性&#xff08;Atomicity&#xff09;&#xff1a;操作这些指令时&#xff0c;要么全部执行成功&#xff0c;要么全部不执行。只要…

JUC 学习笔记

JUC 学习笔记 本文为笔者对 JUC 的学习记录&#xff0c;主要参考了尚硅谷的 JUC 教程 文章目录 JUC 学习笔记1. JUC 概述什么是 JUC&#xff1f;线程和进程&#xff1a;进程的状态&#xff1a;wait 和 sleep&#xff1a;并发和并行&#xff1a;管程&#xff1a;用户线程和守护…

Python numpy - 数组的向量化运算

目录 一 函数numpy.where 二 数组统计方法 1 基础数组统计方法 2 布尔值数组的方法 三 排序 1 函数sort 2 函数np.unique 向量化&#xff1a;利用数组表达式来替代显式循环的方法 一 函数numpy.where 条件逻辑函数 where(condition, x, y) condition为条件,当满足条件…

有限状态机器测试(过渡游览法)

有限状态机器测试(过渡游览法) 测试相关概念—故障类型 行为由转换定义。 故障分为三种类型&#xff1a; 输出错误Output faults&#xff1a;错误的输出是由转换产生的。 状态转移错误State transfer faults&#xff1a;转移到错误的状态。 额外的状态Extra states&#xff08…

医院检验科检验系统(LIS)源码:临检、生化、免疫、微生物

一、检验科检验系统 &#xff08;LIS&#xff09;概述&#xff1a;对接HIS&#xff0c;医生工作站能够方便、及时的查阅患者检验报告。 二、检验科检验系统 &#xff08;LIS&#xff09;主要功能描述&#xff1a; 1.质控品管理&#xff1a; 医院设备质控&#xff08;编码、设…

数据结构入门7-2(散列表)

注 本笔记参考&#xff1a;《数据结构&#xff08;C语言版&#xff09;&#xff08;第2版&#xff09;》 散列表的基本概念 像基于线性表、数表的查找方式&#xff0c;往往都是以关键字的比较为基础的。这种比较方式在遇到结点数量很多的情况时就会暴露其的弊端&#xff1a;需要…

vue3-实战-03-管理后台路由分析搭建和登录模块开发

1-路由的搭建 先分析模块&#xff0c;搭建一些基础的路由信息&#xff0c;我们分析需要登录页面路由&#xff0c;登录成功后展示数据路由&#xff0c;404页面路由&#xff0c;以及我们输入错误的路径的我们需要重定向到404路由。配置好相关路由之后&#xff0c;我们需要在入口文…

基于 ESP32-H2 构建高性价比的安全物联网设备

如今&#xff0c;人们对网联设备安全性的认识和关注不断增加。在欧盟《网络弹性法案》也即将生效的&#xff0c;为网联设备增加硬件层面的安全功能非常重要。 乐鑫 ESP32-H2 致力于为所有开发者提供高性价比的安全解决方案&#xff0c;这款芯片经过专门设计以集成多种安全功能…

【java】java访问https并验证账号密码

java访问https&#xff0c;获取页面或者数据时&#xff0c;需要证书和账号密码的验证。 一 获取CRT证书 获取网站的证书&#xff0c;拿到证书后可能是crt格式&#xff0c;可以使用下面的命令转为p12格式 openssl pkcs12 -export -in Mycert.crt -inkey Mykey.key -out Mycer…

0基础学习VR全景平台篇第35篇:场景功能-细节

本期为大家带来蛙色VR平台&#xff0c;场景管理—细节功能操作。 功能位置示意 一、本功能将用在哪里&#xff1f; 细节功能&#xff0c;用于展示VR漫游作、大像素、高清矩阵作品中细微不容易发现&#xff0c;但却又比较有亮点或者能起到关键作用的画面。 通过在全景图中添加…

javaWeb ssh课程资源网站myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh课程资源网站系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7.…

字节的测试面试题,你觉得很难吗?不是有手就行....

年前的时候&#xff0c;我的一个粉丝跟我说&#xff0c;他在面试美团的自动化测试岗的时候&#xff0c;不幸挂掉了。 越想越可惜&#xff0c;回想面试经过&#xff0c;好好总结了几个点&#xff0c;发现面试没过的主要原因是在几个关键的问题没有给到面试官想要的答案 字节的…

6-HEX, SE,引物荧光标记试剂,胺反应性琥珀酰亚胺酯,广泛用于nucleic acid测序和相关研究

6-HEX, SE | 6-HEX,SE [6-羧基-2&#xff0c;4,4&#xff0c;5&#xff0c;7,7-六氯荧光素&#xff0c;琥珀酰亚胺酯] |CAS&#xff1a;N/A | 纯度&#xff1a;95% &#xff08;文章资料汇总来源于&#xff1a;陕西新研博美生物科技有限公司小编MISSwu&#xff09;​ 结构…

Android Cannot resolve symbol 找不到资源解决方案

一、介绍 在android开发中&#xff0c;我们经常会发现依赖了系统提供的库&#xff0c;但是无法引用&#xff0c;或者找不到&#xff0c; Cannot resolve symbol style/Theme.AppCompat.NoActionBar &#xff0c;报红。这些最经典的无法加载。 出现这个原因并不是你项目的原因&…

手把手教你如何正确永久使用Microsoft Office365?

office2019和office 365有什么区别的呢&#xff1f;为什么越来越多的人更加钟爱office365。简单来说office 2019的零售版本属于一次售出永久使用&#xff0c;价格上比较贵&#xff0c;而且功能上也不会再有更新。而office 365是一种基于云的订阅服务&#xff0c;我们花钱买的是…

Datax同步MySQL到ES

Datax同步MySQL到ES 1、在MySQL中建表2、在ES建立索引3、构建从MySQL到ES的Datax的Json任务4、运行mysql2es.json脚本以下是工作中做过的ETL&#xff0c;如有需要&#xff0c;可以私信沟通交流&#xff0c;互相学习&#xff0c;一起进步 1、在MySQL中建表 建表语句 CREATE TABL…

如何在Flutter 中启用空安全

目录 Step 1: 开启空安全设置 Step 2:检查可升级的三方库 Step 3:升级三方库 Step 4:代码适配支持空安全 Step 1: 开启空安全设置 Flutter 2默认启用了空安全&#xff0c;所以通过Flutter 2创建的项目是已经开启了空安全的检查的&#xff0c;不知道自己是什么版本的可以通…

【MCS-51单片机汇编语言】期末复习总结①——常见指令汇总(题型一)

文章目录 七大寻址方式重要指令转移堆栈加减乘除位操作跳转条件转移子程序常考题型 七大寻址方式 立即寻址&#xff1a;通过一个立即数来指定存储单元的地址&#xff0c;例如#41H&#xff1b;寄存器寻址&#xff1a;Rn&#xff08;n0~7)&#xff0c;A&#xff0c;B&#xff0c…

自学软件测试,一般人我劝你还是算了吧

软件测试自学是完全可以的&#xff0c;但是这句话并不代表人人都可以自学。 想转行软件测试&#xff0c;纯自学会遇到以下8个问题&#xff1a; 1、自学需要很高的自律性&#xff0c;你能做到吗&#xff1f; 2、自学在学习过程中会碰到很多困难&#xff0c;你都能解决吗&#xf…

直播入门手册

直播除了带货&#xff0c;现在越来越成了分享知识&#xff0c;增近主播和粉近距离互动的平台。最近看到抖音上越来越多的主播进行编程经验的分享&#xff0c;这是一个很好的传播知识的方式&#xff0c;以前我们学习编程技术一般看视频&#xff0c;其实视频的互动性没有直播那么…