当前位置: 首页 > news >正文

SpringBoot启动流程分析之创建SpringApplication对象(一)

SpringBoot启动流程分析之创建SpringApplication对象(一)

目录:

文章目录

      • SpringBoot启动流程分析之创建SpringApplication对象(一)
          • 1、SpringApplication的构造方法
            • 1.1、推断应用程序类型
            • 1.2、设置Initializers
            • 1.3、设置Listener
            • 1.4、推断main方法所在类

流程分析

1、SpringApplication的构造方法

来看一下在SpringApplication对象的构造方法中都做了哪些事。

public SpringApplication(Class<?>... primarySources) {this(null, primarySources);
}@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;//判断primarySources不能为空Assert.notNull(primarySources, "PrimarySources must not be null");//将primarySources放入SpringApplication的全局变量primarySources,Set集合中this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//从类路径中推断应用程序类型放到SpringApplication的全局变量webApplicationType this.webApplicationType = WebApplicationType.deduceFromClasspath();//从META-INF/spring.factories文件中获取ApplicationContextInitializer接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量initializers,List集合中setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//同上,也是从META-INF/spring.factories文件中获取ApplicationListener接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量listeners,List集合中setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//通过获取当前调用栈,找到入口方法main所在的类,放入SpringApplication的全局变量mainApplicationClass this.mainApplicationClass = deduceMainApplicationClass();
}
1.1、推断应用程序类型
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBFLUX_INDICATOR_CLASS = "org."+ "springframework.web.reactive.DispatcherHandler";private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."+ "web.servlet.DispatcherServlet";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";static WebApplicationType deduceFromClasspath() {//ClassUtils.isPresent()从默认classloader中判断是否存在对应的类型if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;
}

推断逻辑是:

先是判断默认的classloader中是否存在org.springframework.web.reactive.DispatcherHandler、且不存在org.springframework.web.servlet.DispatcherServlet、org.glassfish.jersey.servlet.ServletContainer,如果为true返回WebApplicationType.REACTIVE;

然后循环String数组,判断如果默认的classloader中是否不存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,如果不存在,则认为不是web应用程序,返回WebApplicationType.NONE;

最后是返回WebApplicationType.SERVLET。

三种返回类型的解释如下:

    1、WebApplicationType.NONE:不是web应用程序2、WebApplicationType.SERVLET:基于servlet的Web应用程序运行3、WebApplicationType.REACTIVE:响应式的web应用程序
1.2、设置Initializers

传入参数是class类型即ApplicationContextInitializer.class,最终调用方法是getSpringFactoriesInstances,注意第二个参数,传的是一个空的Class数组,则加载所有的配置的类,方便后续给定限定类型的时候无需再次加载,直接从cache中读取。

SpringFactoriesLoader.loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader):这个方法,其作用是使用给定的类加载器从“META-INF/spring.factories”加载给定类型的工厂实现的完全限定类名,即得到所有ApplicationContextInitializer接口实现类的完全限定类名,去重。

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {return getSpringFactoriesInstances(type, new Class<?>[] {});}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates//得到所有ApplicationContextInitializer接口的实现类Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//创建所有ApplicationContextInitializer接口实现类的实例List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);//根据order排序AnnotationAwareOrderComparator.sort(instances);return instances;
}

随后当做参数传递到createSpringFactoriesInstances方法中,这个方法主要作用就是根据传入的type类型,parameterTypes参数类型(空Class数组)以及得到的完全限定类名集合创建对象实例,其中getDeclaredConstructor方法作用是得到指定参数类型的构造方法,parameterTypes为空数组即的得到的就是默认构造方法。构造方法基本都是空的,所以无需关心创建Initializers实例的时候在构造方法中执行了什么操作。这些对象的initialize方法会在后面的run方法中被调用。

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,Set<String> names) {//new 一个跟检索出来的接口实现类相同size的ListList<T> instances = new ArrayList<>(names.size());for (String name : names) {try {//通过类加载器加载类Class<?> instanceClass = ClassUtils.forName(name, classLoader);//判断是否为ApplicationContextInitializer接口的实现类Assert.isAssignable(type, instanceClass);//得到指定参数类型的构造方法Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);//创建对象T instance = (T) BeanUtils.instantiateClass(constructor, args);//放到List中,返回instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;
}

spring.factories文件内容中Initializers如下。

在这里插入图片描述
在这里插入图片描述

1.3、设置Listener

这一步跟上面设置Initializers执行的操作是一样的。spring.factories文件内容中Listener如下。
在这里插入图片描述

整理一下Listener对应的Event

监听器事件类型
BackgroundPreinitializerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent、ApplicationFailedEvent、ApplicationContextInitializedEvent
ClearCachesApplicationListenerContextRefreshedEvent
ParentContextCloserApplicationListenerParentContextAvailableEvent
FileEncodingApplicationListenerApplicationEnvironmentPreparedEvent
AnsiOutputApplicationListenerApplicationEnvironmentPreparedEvent
ConfigFileApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent
DelegatingApplicationListenerApplicationEnvironmentPreparedEvent
ClasspathLoggingApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationFailedEvent
LoggingApplicationListenerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent、ApplicationFailedEvent
LiquibaseServiceLocatorApplicationListenerApplicationStartingEvent
1.4、推断main方法所在类

StackTraceElement数组包含了StackTrace(堆栈轨迹)的内容,通过遍历它可以得到当前方法以及其定义类、调用行数等信息。
在这里插入图片描述

private Class<?> deduceMainApplicationClass() {try {//获取StackTraceElement数组,也就是这个栈的信息。StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {//stackTraceElement.getClassName(),得到定义类,即main方法所在类return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;
}

SpringApplication对象的创建过程就完成了。

Springboot 启动还可以使用流式API SpringApplicationBuilder的方式启动:

@SpringBootApplication
public class RegisterApplication {public static void main(String[] args) {new SpringApplicationBuilder(RegisterApplication.class)// 设置当前应用类型.web(WebApplicationType.SERVLET)// 设置 banner 横幅打印方式、有关闭、日志、控制台.bannerMode(Banner.Mode.OFF)// 设置自定义的 banner.banner()// 追加自定义的 initializer 到集合中 .initializers()// 追加自定义的 listeners 到集合中.listeners().run(args);}
}

相关文章:

SpringBoot启动流程分析之创建SpringApplication对象(一)

SpringBoot启动流程分析之创建SpringApplication对象(一) 目录&#xff1a; 文章目录 SpringBoot启动流程分析之创建SpringApplication对象(一)1、SpringApplication的构造方法1.1、推断应用程序类型1.2、设置Initializers1.3、设置Listener1.4、推断main方法所在类 流程分析…...

SSH简介 特点以及作用

引言 SSH&#xff08;Secure Shell&#xff09;是一种用于安全远程访问和数据传输的网络协议。它提供了一种安全的机制&#xff0c;使得用户可以在不安全的网络中安全地进行远程登录、命令执行和文件传输。SSH通过加密技术和认证机制来保护数据的安全性&#xff0c;防止数据在…...

MQTT服务搭建及python使用示例

1、MQTT协议 1.1、MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的、基于发布/订阅模式的通信协议&#xff0c;通常用于物联网设备之间的通讯。它具有低带宽、低功耗和开放性等特点&#xff0c;适合在网络带宽有限或者网络连接不稳定…...

Ubuntu如何设置中文输入法

概述 Ubuntu 是一个基于 Debian 构建的开源操作系统&#xff0c;拥有广泛的用户群体和强大的社区支持。是免费、开源的操作系统。被设计为一个适用于个人电脑、服务器和云平台的通用操作系统。Ubuntu的目标是提供一个稳定、易于使用和免费的操作系统&#xff0c;以促进人们在计…...

PostgreSQL的pg_dump和 pg_dumpall 异同点

PostgreSQL的pg_dump和 pg_dumpall 异同点 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777pg_dump 和 pg_dum…...

【Ping】Windows 网络延迟测试 ping 、telnet、tcping 工具

ping 命令 属于网络层的ICMP协议&#xff0c;只能检查 IP 的连通性或网络连接速度&#xff0c; 无法检测IP的端口状态。 telnet telnet命令&#xff0c;属于应用层的协议&#xff0c;用于远程登录&#xff0c;也可用于检测IP的端口状态。但是功能有限&#xff0c;只能检测一时…...

DuDuTalk:4G桌面拾音设备在银行网点服务场景的应用价值

随着科技的飞速发展&#xff0c;银行业也在不断地寻求创新以提高服务质量和效率。在这个过程中&#xff0c;4G桌面拾音设备作为一种新型的智能设备&#xff0c;其在银行网点服务场景中的应用价值逐渐凸显出来。本文将从多个角度探讨4G桌面拾音设备在银行网点服务场景的应用价值…...

QT 设置窗口不透明度

在窗口作为子窗口时&#xff0c;setWindowOpacity设置窗口的不透明度可能会失效。 QGraphicsOpacityEffect *opacityEffect new QGraphicsOpacityEffect(this); opacityEffect->setOpacity(1.0); this->setGraphicsEffect(opacityEffect);// 创建属性动画对象&#xff…...

如何在Python中实现文本相似度比较?

在Python中实现文本相似度比较可以通过多种方法&#xff0c;每种方法都有其适用场景和优缺点。以下是一些常见的文本相似度比较方法&#xff1a; 1. 余弦相似度&#xff08;Cosine Similarity&#xff09; 余弦相似度是通过计算两个向量之间夹角的余弦值来确定它们之间的相似…...

韩顺平0基础学Java——第7天

p110-p154 控制结构&#xff08;第四章&#xff09; 多分支 if-elseif-else import java.util.Scanner; public class day7{public static void main(String[] args) {Scanner myscanner new Scanner(System.in);System.out.println("input your score?");int s…...

性能远超GPT-4!谷歌发布Med-Gemini医疗模型;李飞飞首次创业瞄准空间智能;疫苗巨头联合OpenAl助力AI医疗...

AI for Science 企业动态速览—— * 谷歌 Med-Gemini 医疗 AI 模型性能远超 GPT-4 * 斯坦福李飞飞首次创业瞄准「空间智能」 * 疫苗巨头 Moderna 与 OpenAl 达成合作 * 美国能源部推动 AI 在清洁能源领域的应用 * 美年健康荣获「2024福布斯中国人工智能创新场景应用企业TOP10」…...

中国科技大航海时代,“掘金”一带一路

文&#xff5c;白 鸽 编&#xff5c;王一粟 “这不就是90年代的内地吗&#xff1f;” 在深度考察完沙特市场后&#xff0c;华盛集团联合创始人兼CEO张霆对镜相工作室感慨道。 在张霆看来&#xff0c;沙特落后的基建&#xff08;意味着大量创新空间&#xff09;、刚刚开放…...

ffmpeg7.0 flv支持hdr

ffmpeg7.0 flv支持hdr 自从ffmpeg6.0应用enhance rtmp支持h265/av1的flv格式后&#xff0c;7.0迎来了flv的hdr能力。本文介绍ffmpeg7.0如何支持hdr in flv。 如果对enhance rtmp如何支持h265不了解&#xff0c;推荐详解Enhanced-RTMP支持H.265 1. enhance rtmp关于hdr 文档…...

【教程】极简Python接入免费语音识别API

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;请不吝给个[点赞、收藏、关注]哦~ 安装库&#xff1a; pip install SpeechRecognition 使用方法&#xff1a; import speech_recognition as srr sr.Recognizer() harvard sr…...

详解typora配置亚马逊云科技Amazon S3图床

欢迎免费试用亚马逊云科技产品&#xff1a;https://mic.anruicloud.com/url/1333 当前有很多不同的博客社区&#xff0c;不同的博客社区使用的编辑器也不尽相同&#xff0c;大概可以分为两种&#xff0c;一种是markdown格式&#xff0c;另外一种是富文本格式。例如华为云开发者…...

Python sqlite3库 实现 数据库基础及应用 输入地点,可输出该地点的爱国主义教育基地名称和批次的查询结果。

目录 【第11次课】实验十数据库基础及应用1-查询 要求: 提示: 运行结果&#xff1a; 【第11次课】实验十数据库基础及应用1-查询 声明&#xff1a;著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 1.简答题 数据库文件Edu_Base.db&#…...

iOS-SSL固定证书

文章目录 1. SSL简介2. 证书锁定原理1.1 证书锁定1.2 公钥锁定1.3 客户端获取公钥1.4 客户端使用SSL锁定选择1.5 项目支持SSL证书锁定1.6 问题记录1. SSL简介 证书锁定(SSL/TLS Pinning)顾名思义,将服务器提供的SSL/TLS证书内置到移动端开发的APP客户端中,当客户端发起请求…...

docker 开启 tcp 端口

前言&#xff1a;查了很多网上资料 都说要修改daemons,json 完全不管用&#xff0c;而且还导致添加 {“host”:["tcp://0.0.0.0:2375","unix:///var/lib/docker.sock"]} 后&#xff0c;docker restart 失败&#xff0c;浪费了不少时间 &#xff01;&am…...

zookeeper之分布式环境搭建

ZooKeeper的分布式环境搭建是一个涉及多个步骤的过程&#xff0c;主要包括准备工作、安装ZooKeeper、配置集群、启动服务以及验证集群状态。以下是搭建ZooKeeper分布式环境的基本步骤&#xff1a; 1. 准备工作 确保所有节点的系统时间同步。确保所有节点之间网络互通&#xf…...

java设计模式三

工厂模式是一种创建型设计模式&#xff0c;它提供了一个创建对象的接口&#xff0c;但允许子类决定实例化哪一个类。工厂模式有几种变体&#xff0c;包括简单工厂模式、工厂方法模式和抽象工厂模式。下面通过一个简化的案例和对Java标准库中使用工厂模式的源码分析来说明这一模…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...