SpringBoot启动流程及自动配置
SpringBoot启动流程源码:
1、启动SpringBoot启动类SpringbootdemoApplication中的main方法。
@SpringBootApplication
public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplication.class, args);}
}
2、调用SpringApplication.run(SpringbootdemoApplication.class, args),该方法是一个静态方法。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}
3、继续调用SpringApplication内部的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}
,且构建了一个SpringApplication对象,应用程序将从指定的主要来源加载Bean
public SpringApplication(Class<?>... primarySources) {this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {//resourceLoader赋值为Nullthis.resourceLoader = resourceLoader;//primarySources不为空,继续向下执行。为空抛异常Assert.notNull(primarySources, "PrimarySources must not be null");//将SpringbootdemoApplication(启动类)赋值给primarySources this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//从classpath类路径推断Web应用类型,有三种Web应用类型,分别是//NONE: 该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器//SERVLET: 该应用程序应作为基于 servlet 的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。//REACTIVE: 该应用程序应作为响应式 Web 应用程序运行,并应启动嵌入式响应式 Web 服务器this.webApplicationType = WebApplicationType.deduceFromClasspath();//初始化bootstrapRegistryInitializers,通过getSpringFactoriesInstances()获取工厂实例,//底层使用的是反射Class<?> instanceClass = ClassUtils.forName(name, classLoader)动态加载实例对象。this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));//初始化ApplicationContextInitializer集合setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//初始化ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法的类。this.mainApplicationClass = deduceMainApplicationClass();
}
4、运行 Spring 应用程序,创建并刷新一个新的 ApplicationContext。
public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();//通过BootstrapRegistryInitializer来initialize默认的DefaultBootstrapContextDefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;//配置java.awt.headless属性configureHeadlessProperty();//获取SpringApplicationRunListeners监听器SpringApplicationRunListeners listeners = getRunListeners(args);//启动SpringApplicationRunListeners监听,表示SpringApplication启动(触发ApplicationStartingEvent事件)listeners.starting(bootstrapContext, this.mainApplicationClass);try {//创建ApplicationArguments对象,封装了args参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//做相关环境准备,绑定到SpringApplication,返回可配置环境对象ConfigurableEnvironment ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);//配置spring.beaninfo.ignore,设置为true.即跳过搜索Bean信息configureIgnoreBeanInfo(environment);//控制台打印SpringBoot的Banner(横幅)标志Banner printedBanner = printBanner(environment);//根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContextcontext = createApplicationContext();//设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartupcontext.setApplicationStartup(this.applicationStartup);//应用所有的ApplicationContextInitializer容器初始化器初始化context,触发ApplicationContextInitializedEvent事件监听,打印启动日志信息,启动Profile日志信息。//ConfigurableListableBeanFactory中注册单例Bean(springApplicationArguments),并为该BeanFactory中的部分属性赋值。//加载所有的source.并将Bean加载到ConfigurableApplicationContext,触发ApplicationPreparedEvent事件监听prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//刷新容器(在方法中集成了Web容器具体请看 https://editor.csdn.net/md/?articleId=123136262)refreshContext(context);//刷新容器的后置处理(空方法)afterRefresh(context, applicationArguments);//启动花费的时间Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {//打印日志Started xxx in xxx seconds (JVM running for xxxx)new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}//触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。listeners.started(context, timeTakenToStartup);//调用ApplicationRunner和CommandLineRunnercallRunners(context, applicationArguments);}//处理运行时发生的异常,触发ApplicationFailedEvent事件监听catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {//启动准备消耗的时间Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);//在run方法完成前立即触发ApplicationReadyEvent事件监听,表示应用上下文已刷新,并且CommandLineRunners和ApplicationRunners已被调用。listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}
SpringBoot的自动配置:
SpringBoot的启动类上总是有@SpringBootApplication这个注解。接下来我们来了解一下这个注解。进入@SpringBootApplication源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {......
}
由此可见,@SpringBootApplication注解是一个组合注解,由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan注解组成。
@SpringBootConfiguration其实就是一个@Configuration,表明这是一个配置类,可以向容器注入组件。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {......
}
@EnableAutoConfiguration由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})注解组成
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {......
}
@AutoConfigurationPackage内部用到了@Import导入Registrar
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {......
}
Registrar实现了ImportBeanDefinitionRegistrar接口,因此可将组件都扫描注冊到 spring 容器中
static class Registrar implements ImportBeanDefinitionRegistrar,
DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new
AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new
String[0]));
}
public Set determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
@AutoConfigurationPackage将主配置类(@SpringBootApplication
标注的类)所在包下的所有组件都扫描注册到Spring容器中。
@Import({AutoConfigurationImportSelector.class}) 将AutoConfigurationImportSelector(自动配置导入选择器)导入容器中
AutoConfigurationImportSelector类中的selectImports()方法的作用是选择导入过滤后的自动配置
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
getAutoConfigurationEntry(AnnotationMetadata
annotationMetadata)根据annotationMetadata(即我们的启动类SpringbootdemoApplication)获取AutoConfigurationEntry
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {//获取注解属性AnnotationAttributes attributes = this.getAttributes(annotationMetadata);//从META-INF/spring.factories中获取候选配置。List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//去除重复配置configurations = this.removeDuplicates(configurations);//获取注解中的排除项Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);//检查排除类this.checkExcludedClasses(configurations, exclusions);//从上面的候选配置中移除所有排除的配置类configurations.removeAll(exclusions);//通过ConfigurationClassFilter筛选配置configurations = this.getConfigurationClassFilter().filter(configurations);//触发自动配置导入事件this.fireAutoConfigurationImportEvents(configurations, exclusions);//返回排除后的自动配置Entryreturn new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}
总结SpringBoot启动原理
1、调用有@SpringBootApplication注解的启动类的main方法
2、通过调用SpringApplication内部的run()方法构建SpringApplication对象。
创建SpringApplication对象:
2.1 PrimarySources 不为空,将启动类赋值给primarySources 对象。
2.2 从classpath类路径推断Web应用类型,有三种Web应用类型NONE、SERVLET、REACTIVE
2.3 初始化bootstrapRegistryInitializers
2.4 初始化ApplicationContextInitializer集合
2.5 初始化ApplicationListener
2.6 获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法A的。 3、调用SpringBootApplication的run方法。 4、long startTime = System.nanoTime();
记录项目启动时间。 5、通过BootstrapRegistryInitializer来初始化DefaultBootstrapContext
6、getRunListeners(args)获取SpringApplicationRunListeners监听器 7、
listeners.starting()触发ApplicationStartingEvent事件
8、prepareEnvironment(listeners, bootstrapContext,
applicationArguments) 将配置文件读取到容器中,返回ConfigurableEnvironment 对象。
9、printBanner(environment) 打印Banner图,即SpringBoot启动时的图案。
10、根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContext,并设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartup
11、
调用prepareContext()初始化context等,打印启动日志信息,启动Profile日志信息,并为BeanFactory中的部分属性赋值。
12、刷新容器,在该方法中集成了Tomcat容器 13、加载SpringMVC.
14、刷新后的方法,空方法,给用户自定义重写afterRefresh() 15、Duration timeTakenToStartup =
Duration.ofNanos(System.nanoTime() - startTime)算出启动花费的时间。
16、打印日志Started xxx in xxx seconds (JVM running for xxxx)
17、listeners.started(context,
timeTakenToStartup)触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。
18、调用ApplicationRunner和CommandLineRunner 19、返回上下文。
相关文章:
SpringBoot启动流程及自动配置
SpringBoot启动流程源码: 1、启动SpringBoot启动类SpringbootdemoApplication中的main方法。 SpringBootApplication public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplication.class, …...

【Linux】进程轻松入门
目录 一, 冯* 诺依曼体系结构 1,存储结构 编辑 二, 操作系统 1,概念 2,设计OS的目的 3,定位 4,如何理解 "管理" 5, 总结 三,进程 1. 概念 那么…...

【使用时空RBF-NN进行非线性系统识别】实现了 RBF、分数 RBF 和时空 RBF 神经网络,用于非线性系统识别研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Tomcat 安装配置教程及成功后,启动失败报错解决方案
解决方案 我的报错原因是因为我的JDK是1.8的而我的Tomcat是10版本的,可能是因为版本原因吧,我重新装了Tomcat 9就可以启动成功了! 简单说下安装的时候需要注意哪些步骤吧 今天我在安装tomcat10的时候,安装成功后,启…...

C#文件操作从入门到精通(2)——查看某个dll中有哪些函数
kernel32.dll中含有ini文件操作使用的函数,我们可以通过VisualStudio自带的dumpbin.exe查看dll所包含的函数,操作步骤如下: 1、找到dumpbin.exe所在的文件夹 我的电脑中安装了VisualStudio2019社区版以及VisualStudio2017Professional,但是我发现VisualStudio2019社区版中…...

二分查找算法(全网最详细代码演示)
二分查找也称 半查找(Binary Search),它时一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字 有序 排列。 注意:使用二分查找的前提是 该数组是有序的。 在实际开…...

draw up a plan
爱情是美好的,却不是唯一的。爱情只是属于个人化的感情。 推荐一篇关于爱情的美文: 在一个小镇上,有一家以制作精美巧克力而闻名的手工巧克力店,名叫“甜蜜之爱”。这家巧克力店是由一位名叫艾玛的年轻女性经营的,她对…...

抖音seo源码开发源代码开发技术分享
一、 抖音SEO源码开发,需要掌握以下技术: 抖音API接口:抖音提供了丰富的API接口,包括用户信息、视频信息、评论信息等。 数据爬取技术:通过抓包分析抖音接口的数据结构,可以使用Python等编程语言编写爬虫程…...
QEMU(Quick Emulator)
QEMU(Quick Emulator)是一款由法布里斯贝拉等人编写的免费的可执行硬件虚拟化的开源托管虚拟机。它可以通过动态的二进制转换模拟CPU,并提供一组设备模型,使它能够运行多种未修改的客户机OS。QEMU还可以为user-level的进程执行CPU…...
Gateway结合nacos(lb://xxx)无效问题
Gateway结合nacos无效 版本如下: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2021.0.1.0 org.springframework.cloud:spring-cloud-starter-gateway:3.1.1 配置如下: server:port: 7000 spring:application:name: springCloudGa…...

NODEJS笔记
全局对象 global/window console.log/info/warn/error/time/timeEnd process.arch/platform/version/env/kill/pid/nextTick Buffer.alloc(5,abcde) String/toString setTimeout/clearTimeout setInterval/clearInterval setImmediate/clearImmediate process.nextTi…...
无涯教程-jQuery - html( )方法函数
html(val)方法获取第一个匹配元素的html内容(innerHTML)。此属性在XML文档上不可用。 html( ) - 语法 selector.html( ) html( ) - 示例 以下是一个简单的示例,简单说明了此方法的用法- <html><head><title>The jQuery Example</title>…...

Linux vsftp三种模式的简单配置部署
环境:Debian 6.1.27-1kali1 (2023-05-12) vsftpd 安装 --查看是否当前系统是否已安装 apt list --installed | grep vsftpd 没有安装的话,就正常安装 apt-get update apt-get install vsftpd 一、匿名用户模式 分享一些不重要文件,任…...

6.1.tensorRT高级(1)-概述
目录 前言1. tensorRT高级概述总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 tensorRT 高级-概述 课程大纲可看下面的思维…...
【Python】将M4A\AAC录音文件转换为MP3文件
文章目录 m4aaac 基础环境: sudo apt-get install ffmpegm4a 要将M4A文件转换为MP3文件,你可以使用Python中的第三方库pydub。pydub使得音频处理变得非常简单。在开始之前,请确保你已经安装了pydub库,如果没有,可以通…...
个性新颖纯css手风琴效果选项卡
当涉及到个性新颖的纯CSS手风琴效果选项卡时,有多种方法可以实现。以下是三种可能的方法: 三种方法实现 方法一:使用:target伪类和CSS过渡效果 <style>.accordion {width: 300px;}.accordion-item {overflow: hidden;max-height: 0;…...
js的sendBeacon方法介绍
js的sendBeacon方法介绍 Beacon API是一种轻量级且有效的将网页活动记录到服务器的方法。它是一个 JavaScript API,可帮助开发人员将少量数据(例如分析或跟踪信息、调试或诊断数据)从浏览器发送到服务器。 在本文中,我们将介绍B…...

【Tomcat---1】IDEA控制台tomcat日志输出乱码解决
一、修改IDEA的文件编码配置为UTF-8 二、修改IDEA的vmoptions文件,添加-Dfile.encodingUTF-8 到Tomcat目录/conf文件夹修改logging.properties 重启idea即可。采用统一的编码...
Redis学习路线(2)—— Redis的数据结构
一、Redis的数据结构 Redis是一个Key-Value的数据库,key一般是String类型,不过Value的类型却有很多: String: Hello WorldHash: {name: "jack", age: 21}List: [A -> B -> C -> C]Set…...

【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(持久化功能分析)
探究Redis服务启动的过程机制的技术原理和流程分析的指南(持久化功能分析) Redis提供的持久化机制Redis持久化如何工作Redis持久化的故障分析持久化频率操作分析数据库多久调用一次write,将数据写入内核缓冲区?内核多久将系统缓冲…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...

对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
1.题目描述 2.思路 当前的元素可以重复使用。 (1)确定回溯算法函数的参数和返回值(一般是void类型) (2)因为是用递归实现的,所以我们要确定终止条件 (3)单层搜索逻辑 二…...