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,将数据写入内核缓冲区?内核多久将系统缓冲…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
