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、返回上下文。

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

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

相关文章

ubuntu 配置NAT配置内网网关服务器

本次完全参考 Ubuntu20.04服务器开启路由转发让局域网内其他电脑通过该服务器连接外网 ubuntu 官方防火墙文档 Security - Firewall 本人从前的操作 Ubuntu 18.04 通过 ufw route 配置网关服务器 网关 配置内网DNS 服务器 背景知识 从前总认为既然UFW简化了 iptables &…

Eureka 学习笔记2:客户端 DiscoveryClient

版本 awsVersion ‘1.11.277’ DiscoveryClient # cacheRefreshTask // 配置shouldFetchRegistry if (clientConfig.shouldFetchRegistry()) {// 配置client.refresh.intervalint registryFetchIntervalSeconds clientConfig.getRegistryFetchIntervalSeconds();// 配置expB…

深入理解Zookeeper分布式锁的概念及原理

深入理解Zookeeper分布式锁的概念及原理 1. 引言 在分布式系统中&#xff0c;锁是一种重要的机制&#xff0c;用于协调多个节点之间的并发访问。在大规模分布式系统中&#xff0c;实现高效且可靠的分布式锁是一个挑战。本篇博客将深入探讨Zookeeper分布式锁的概念及其原理&am…

HTML+CSS+JavaScript:轮播图的自动播放、手动播放、鼠标悬停暂停播放

一、需求 昨天我们做了轮播图的自动播放&#xff0c;即每隔一秒自动切换一次 今天我们增加两个需求&#xff1a; 1、鼠标点击向右按钮&#xff0c;轮播图往后切换一次&#xff1b;鼠标点击向左按钮&#xff0c;轮播图往前切换一次 2、鼠标悬停在轮播图区域中时&#xff0c;…

Verilog语法学习——LV5_位拆分与运算

LV5_位拆分与运算 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 现在输入了一个压缩的16位数据&#xff0c;其实际上包含了四个数据…

Layui网页模板

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>会员管理</title><link href"layui/c…

JAVA IO 的 Windows、Linux文件路径差异

因为文件路径是字符串拼接的&#xff0c;所以之前的路径是这样 D:\pics/bankslip/2023/08/01\fe68a2c16ecb498f89a88f9472a1361e.png /root/temp/bankslip/2023/08/01\fe68a2c16ecb498f89a88f9472a1361e.png 左斜杠、右斜杠都有的情况 那么在windows中这样的路径是正常的&#…

ComPDFKit 转档SDK OCR表格识别功能

我们非常高兴地宣布&#xff0c;适用于 Windows、iOS、Android 和服务器的 ComPDFKit 转档SDK 1.8.0 现已发布&#xff01;在该版本中&#xff0c;OCR 功能支持了表格识别&#xff0c;优化了OCR文字识别率。PDF to HTML 优化了html 文件结构&#xff0c;使转换后的 HTML 文件容…

css 四角边框移动效果

块是长宽相等的正方形&#xff0c;大小浏览器分辨率变化而变化利用平移变化translate来时实现边框到达鼠标划到的块&#xff0c;坐标是鼠标滑到块的offsetLeft和offsetTop <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&quo…

誉天程序员-SpringMVC回顾:五种接参方式

1、GET查询串传参&#xff1a; 2、RESTFul形式传参&#xff08;参数少&#xff09; 3、form表单传参 4、混合传参&#xff0c;查询串表单 5、终极王者&#xff0c;json传参&#xff08;参数多&#xff09; package com.book.admin.controller;import com.book.admin.entity.Us…

从互联网到云时代,Apache RocketMQ 是如何演进的?

作者&#xff1a;隆基 2022 年&#xff0c;RocketMQ 5.0 的正式版发布。相对于 4.0 版本而言&#xff0c;架构走向云原生化&#xff0c;并且覆盖了更多业务场景。 消息队列演进史 操作系统、数据库、中间件是基础软件的三驾马车&#xff0c;而消息队列属于最经典的中间件之一…

用python需要下载软件吗,python需要安装哪些软件

大家好&#xff0c;本文将围绕安装python需要什么样的电脑配置展开说明&#xff0c;python需要安装哪些软件是一个很多人都想弄明白的事情&#xff0c;想搞清楚用python需要下载软件吗需要先了解以下几个事情。 编程这东西很神奇。对于那些知道如何有用和有趣的这个工具,对于Xi…

Windows 实例如何开放端口

矩池云 Windows 实例相比于 Linux 实例&#xff0c;除了在租用机器的时候自定义端口外&#xff0c;还需要在 Windows防火墙中添加入口规则。接下来将教大家如何设置 Windows 防火墙&#xff0c;启用端口。 租用成功后通过 RDP 链接连接服务器&#xff0c;然后搜索防火墙&#x…

React的UmiJS搭建的项目集成海康威视h5player播放插件H5视频播放器开发包 V2.1.2

最近前端的一个项目&#xff0c;大屏需要摄像头播放&#xff0c;摄像头厂家是海康威视的&#xff0c;网上找了一圈都没有React集成的&#xff0c;特别是没有使用UmiJS搭脚手架搭建的&#xff0c;所以记录一下。 海康威视的开放平台的API地址&#xff0c;相关插件和文档都可以下…

使用 docker-compose 一键部署多个 redis 实例

目录 1. 前期准备 2. 导入镜像 3. 部署redis master脚本 4. 部署redis slave脚本 5. 模板文件 6. 部署redis 7. 基本维护 1. 前期准备 新部署前可以从仓库&#xff08;repository&#xff09;下载 redis 镜像&#xff0c;或者从已有部署中的镜像生成文件&#xff1a; …

简单的python有趣小程序,有趣的代码大全python

这篇文章主要介绍了python简单有趣的程序源代码&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。

网络防御技术:防火墙与入侵检测系统

目录 简介&#xff1a; 1. 防火墙基础 1.1 类型 1.2 防火墙规则配置 2. 入侵检测系统&#xff08;IDS&#xff09;基础 2.1 类型 2.2 IDS规则配置 总结 简介&#xff1a; 在当今数字化时代&#xff0c;网络安全威胁日益增加&#xff0c;保护网络免受恶意攻击变得尤为重…

安装 mysql8.0-docker版安装

一、docker安装 安装docker2022版&#xff08;对大家有帮助&#xff09; 二、docker 安装mysql8.0 1.从 Docker Hub 下载 MySQL 8.0 镜像。打开终端并运行以下命令&#xff1a; docker pull mysql:8.0 2.创建一个 MySQL 容器并运行。在终端中运行以下命令&#xff1a; docke…

Hadoop学习日记-YARN组件

YARN(Yet Another Resource Negotiator)作为一种新的Hadoop资源管理器&#xff0c;是另一种资源协调者。 YARN是一个通用的资源管理系统和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度 YARN架构图 YARN3大组件&#xff1a; &#xff08;物理层面&#xff09…

ICML 2023 | 拓展机器学习的边界

编者按&#xff1a;如今&#xff0c;机器学习已成为人类未来发展的焦点领域&#xff0c;如何进一步拓展机器学习技术和理论的边界&#xff0c;是一个极富挑战性的重要话题。7月23日至29日&#xff0c;第四十届国际机器学习大会 ICML 2023 在美国夏威夷举行。该大会是由国际机器…