Spring Boot 源码--SpringApplication#run 方法源码解析

news2024/10/5 23:16:21

前言:

开发过 Spring Boot 项目的都知道只需要一个简单的入口类,然后入口类中有个 main 方法,main 方法中调用了 SpringApplication.run 方法,再配合 @SpringBootApplication 注解就可以完成一个项目的启动,如下:

@SpringBootApplication
public class DemoApplication {

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

}

那你知道 SpringApplication#run 具体做了什么吗?

启动流程简单debugg:

在这里插入图片描述

SpringApplication#run 方法源码解析:

SpringApplication#run 方法:

//run 方法的封装 
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class[]{primarySource}, args);
}
//创建 SpringApplication 对象 调用 SpringApplication 的 run 方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return (new SpringApplication(primarySources)).run(args);
}

面两个方法,一个方法完成了 SpringApplication 创建,一个完成了 run 方法的调用,非常简单,具体的调用细节在下面的方法中。

真正的SpringApplication#run 方法:

//真正的SpringApplication run 方法
public ConfigurableApplicationContext run(String... args) {
	//计时器
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	//创建默认的引导上下文 实现了 ConfigurableBootstrapContext 接口
	DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
	//配置应用程序上下文
	ConfigurableApplicationContext context = null;
	//设置系统属性 java.awt.headless 的值 默认值为true 开启headless模式
	this.configureHeadlessProperty();
	//创建监听器  获取运行时的监听器列表
	SpringApplicationRunListeners listeners = this.getRunListeners(args);
	//启动监听器
	listeners.starting(bootstrapContext, this.mainApplicationClass);

	try {
		//初始化默认应用参数类
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//根据监听器 引导上下文 默认参数 准备 spring 环境
		ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		//设置需要忽略的 bean 信息 BeanInfo是一种描述 Java Bean 属性 事件 方法的元数据信息的机制
		this.configureIgnoreBeanInfo(environment);
		//创建打印类 可以自定义启动时候的打印信息
		Banner printedBanner = this.printBanner(environment);
		//创建应用程序上下文
		context = this.createApplicationContext();
		//应用程序上下文设置 applicationStartup
		context.setApplicationStartup(this.applicationStartup);
		//准备上下文--预处理 上面已经创建了应用程序上下文 但是还是一个空白对象 
		this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		//刷新应用上下文 核心方法 spring 源码中会讲解
		this.refreshContext(context);
		//刷新应用上下文后置处理
		this.afterRefresh(context, applicationArguments);
		//即时结束
		stopWatch.stop();
		//日志
		if (this.logStartupInfo) {
			(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
		}
		//广播应用上下文加载完成
		listeners.started(context);
		//调用执行Runner  如果需要 spring boot 项目启动就执行某些操作就可以自定义实现  ApplicationRunner  CommandLineRunner
		this.callRunners(context, applicationArguments);
	} catch (Throwable var10) {
		this.handleRunFailure(context, var10, listeners);
		throw new IllegalStateException(var10);
	}

	try {
		//广播应用上下文运行
		listeners.running(context);
		return context;
	} catch (Throwable var9) {
		this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
		throw new IllegalStateException(var9);
	}
}

//准备应用程序上下文
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	//为应用程序上下文设置环境
	context.setEnvironment(environment);
	//应用程序上下文后处理
	this.postProcessApplicationContext(context);
	//初始化应用程序上下文
	this.applyInitializers(context);
	//监听器广播上下文已经准备好
	listeners.contextPrepared(context);
	//引导上下文关闭
	bootstrapContext.close(context);
	if (this.logStartupInfo) {
		this.logStartupInfo(context.getParent() == null);
		this.logStartupProfileInfo(context);
	}
	//获取应用程序上下文的beanFactory 并设置一些属性
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		//设置单例bean
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}

	if (beanFactory instanceof DefaultListableBeanFactory) {
		//设置bean 定义允许覆盖
		((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}

	if (this.lazyInitialization) {
		//添加延迟加载bean 后置处理器
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}

	Set<Object> sources = this.getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	//加载bean对象到 context中
	this.load(context, sources.toArray(new Object[0]));
	//监听器广播上下文已经加载
	listeners.contextLoaded(context);
}

源码关键对象分析:

  • DefaultBootstrapContext:是 ConfigurableBootstrapContext 接口的实现,在 Spring Boot 应用程序的引导阶段使用,可以通过注册一些自定义的组件,去影响应用程序的启动行为,也是 Spring Boot 程序启动的一个扩展点。
  • ConfigurableApplicationContext:应用程序上下文可配置的接口,开发人员可以对应用程序上下文进行更灵活的配置和可扩展性,以满足不同应用程序的需求,例如设置环境、配置文件位置、添加后置处理器等。它提供了更多的灵活性和可扩展性,以满足不同应用程序的需求。
  • SpringApplicationRunListeners:监听器列表,负责在应用程序运行过程中触发相应的事件和回调函数,通过注册监听器,开发人员可以在应用程序启动、停止、失败等不同的阶段执行自定义的逻辑。
  • ApplicationArguments:实际用的是实现类 DefaultApplicationArguments,是一个参数解析类,ApplicationArguments 接口提供了对应用程序启动参数的访问和解析功能,DefaultApplicationArguments 对象可以方便地获取和解析应用程序启动时传入的参数信息,也可以自己根据需求去扩展。
  • ConfigurableEnvironment:环境配置接口,提供了一种可配置的方式来管理应用程序的配置属性,通过调用 prepareEnvironment(listeners, applicationArguments) 方法,可以根据传入的监听器和应用程序参数来准备环境配置,包括加载配置文件、解析配置等,同样可以根据具体的应用程序需求进行扩展。
  • Banner:接受一个 ConfigurableEnvironment 对象,通过 printBanner 方法,打印出自定义的 Banner(程序启动控制台打印的图标,比如打印一个:永无bug),这个功能没有什么大的作用,是一个提升体验感的小功能。
  • prepareContext:应用程序上下文启动预处理,包括设置环境变量、注册监听器、处理命令行参数等,保证应用程序上下文可以正常启动。
  • refreshContext:刷新应用程序上下文,主要解析配置文件,加载业务 bean等,底层调用的是 Spring 的 refresh 方法。
  • afterRefresh:刷新应用程序上下文后的操作,Spring Boot 没有对其进行实现,应用程序开发者有需要可以自定义一些自己逻辑。
  • listeners.started(context):通知注册的监听器应用程序已经启动完成。
  • this.callRunners(context, applicationArguments):执行应用程序的运行器,比如执行我们自定义的 Runner,例如初始化数据、启动定时任务等,这个还是比较常用的。
  • listeners.running(context):通知注册的监听器应用程序已经开始运行。

总结:习惯了 Spring Boot 的自动装配,一个简单的入口类,就可以帮我们跑起来应用程序,但是我们不知道是怎样启动的,具体都做了什么,阅读了源码之后,我们发现框架帮我们做了很多工作,也给我们留了很多可以扩展的入口,也有我们常用的功能在源码里都有体现,比如我们常用的 Runner 功能,可能在开发中没少用,但是有没有想过为什么可以这么用呢?其实这在源码中都有体现,如果你阅读了源码,就会对这些底层实现了然于胸,本文简单分享了 Spring Boot 启动相关源码,希望帮助到有需要的伙伴们。

欢迎提出建议及对错误的地方指出纠正。

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

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

相关文章

云打印怎么上传文件?云打印怎么把文件发送给商家?

随着云打印的火热&#xff0c;现在很多同学们都选择云打印服务来打印自己的资料文档。但是云打印服务毕竟要隔着网络打印&#xff0c;那么我们应该怎么上传文件呢&#xff1f;云打印怎么把文件发送给商家呢&#xff1f;今天小易就带大家一起来了解一下。 云打印怎么上传文件&am…

python爬虫之xpath4

1 最终项目实现的代码 仙剑 #!/usr/bin/env python ​ import logging import requests import re from urllib.parse import urljoin import pymongo import multiprocessing ​ mongo_client pymongo.MongoClient("mongodb://192.168.135.131:27017/") db mong…

【禅道客户案例】专访鸿泉物联研发副总监徐小倩,感受上市公司研发项目管理“知与行”

杭州鸿泉物联网技术股份有限公司&#xff08;以下简称“鸿泉物联”、“公司”&#xff09;成立于2009年6月11日&#xff0c;2019年11月6日登陆上海证券交易所科创板&#xff08;股票代码&#xff1a;688288&#xff09;&#xff0c;注册资本10034.392万元&#xff0c;目前员工6…

嵌入式4-24

作业&#xff1a; 整理思维导图 定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数&#xff1a; void set_length(int l); //设置长度 void set_width(int w); //设置宽度 int get_length(); //获取长度 int get_width(); //获取宽…

Julia教程(一):变量

1、变量 在 Julia 中&#xff0c;变量是与值关联&#xff08;或绑定&#xff09;的名称。当想要存储一个值&#xff08;例如&#xff0c;经过一些数学运算后获得的值&#xff09;以供以后使用时&#xff0c;它非常有用。例如&#xff1a; # 定义一个变量x&#xff0c;并赋值为…

力扣:82. 删除排序链表中的重复元素 II(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 输入&#xff1a; head [1,2,3…

AXI4---低功耗接口

在电子系统设计中&#xff0c;"low-power interface"&#xff08;低功耗接口&#xff09;是指专为减少能耗而设计的硬件接口。这类接口在不需要牺牲性能的情况下&#xff0c;通过各种技术降低功耗&#xff0c;对于移动设备、嵌入式系统和其他电池供电的应用来说尤其重…

为什么要使用gamma校正?

为什么要使用gamma校正&#xff1f; 人眼直接观察实物相机拍摄、存储、输出到显示器上&#xff0c;人眼再观察该物体1. 拍摄2. 存储3. 显示 总结&#xff1a;在计算机时代早期&#xff0c;为了抵消当时主流的RTC显示器的非线性特征&#xff0c;拍摄图片存储过程中需要gamma校正…

蓝桥杯-网络安全-练习题-crypto-rsa

共模攻击 直接脚本即可 import libnum import gmpy2import random random.seed(123456)e1 random.randint(100000000, 999999999) print(e1) e2 65537 n 7265521127830448713067411832186939510560957540642195787738901620268897564963900603849624938868472135068795683…

【blog项目】layui与jquery冲突导致鼠标悬停事件失效、如何调用layui.use()作用域里的方法

blog项目前台展示——查询数据库中的文章类型并展示时出现的bug 1 正常演示 2 用jquery查询数据库并添加到页面后 3 相关代码 <script src"/static/jquery-2.1.4.js"></script> <script src"/static/layui/layui.js"></script> …

信息系统项目管理师——质量管理论文写法以及子题目

质量管理概论 过程组过程名称含义输入工具和技术输出规划过程组规划质量管理制定质量管理策略、明确质量标准与过程&#xff0c;以确保项目满足既定质量要求。- 项目管理计划- 干系人登记册- 需求文档- 组织过程资产1.专家判断 2.数据收集 标杆对照 头脑风暴 访谈 3.数据分析 …

【网络安全】跨站脚本攻击(XSS)

专栏文章索引&#xff1a;网络安全 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、XSS简介 二、XSS漏洞危害 三、XSS漏洞类型 1.反射型XSS 2.存储型XSS 3.DOM型XSS 四、XSS漏洞防御 一、XSS简介 XSS&#xff08;Cross-Site Scripting&#xff09; XSS 被…

机器视觉系统-工业光源什么是高角度光,以及发光角度得分类

光路描述&#xff1a;光线与水平面角度>45称为高角度光。 效果分析&#xff1a;高角度照射&#xff0c;光线经 被测物表面平整部分反射后进入镜头&#xff0c;图像效果表现为灰度值较高&#xff1b;不平整部分反射光进入不了镜头&#xff0c;图像效果表现为灰度值较低。 主要…

高薪诱惑下,跳槽真的是明智选择吗?

跳槽是职场生涯中不可避免的一部分&#xff0c;许多人在考虑是否换工作时&#xff0c;往往会被潜在的高薪所吸引。然而&#xff0c;决定是否接受一个高薪职位的邀请并非易事&#xff0c;它涉及到多个层面的权衡和考量。 我们要认识到薪酬仅是工作回报的一个方面。虽然高薪资往往…

springboot:java操作docker(docker-java)的基本使用

文章目录 dokcer-java使用xmlping拉取镜像创建容器查看所有容器信息启动容器查看日志删除容器删除镜像 dokcer-java使用 xml 引入依赖 <!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java --> <dependency><groupId>com.github…

【漏洞复现】通天星CMSV6车载监控平台Login弱口令漏洞

漏洞描述&#xff1a; 通天星CMSV6车载定位监控平台拥有以位置服务、无线3G/4G视频传输、云存储服务为核心的研发团队&#xff0c;专注于为定位、无线视频终端产品提供平台服务&#xff0c;通天星CMSV6产品覆盖车载录像机、单兵录像机、网络监控摄像机、行驶记录仪等产品的视频…

智慧物流时代:数字化转型下的物流新篇章

一、什么是智慧物流&#xff1f; 智慧物流是一种利用先进科技和信息技术优化物流供应链系统的新型模式。以数据为核心&#xff0c;智慧物流通过物联网、云计算、大数据和人工智能等技术手段实现物流信息的全面记录、无缝对接和智能化处理。其核心在于实现物流各环节的精细化、…

春游江淮 | 遇见花海, 郎溪第二届绣球花卉节即将开启

郎溪美迪花世界 各种颜色的绣球花海已初露花容 一团团、一簇簇 或淡粉、或淡紫、或淡蓝 以及各种渐变色的花球 景色可谓是浓淡相宜 在暮春时节 2024中国郎溪第二届绣球花卉节 正式官宣啦 郎溪邀您来遇见花海的浪漫 敬请期待吧 活动主题 花开中国梦,绣球美郎川 活动…

springboot 集成 activemq

文章目录 一&#xff1a;说明二&#xff1a;e-car项目配置1 引入activemq依赖2 application启动类配置消息监听3 application.yml配置4 MQConfig.java 配置类5 ecar 项目中的监听6 junit 发送消息 三&#xff1a;tcm-chatgpt项目配置5 MQListener.java 监听消息 测试启动active…

安卓玩机工具推荐----MTK芯片 简单制作线刷包 备份分区 备份基带 去除锁类 推荐工具操作解析

工具说明 在前面几期mtk芯片类玩机工具中解析过如何无官方固件从手机抽包 制作线刷包的步骤&#xff0c;类似的工具与操作有很多种。演示的只是本人片面的理解与一些步骤解析。mtk芯片机型抽包关键点在于..mt*****txt的分区地址段引导和 perloader临时分区引导。前面几期都是需…