SpringBoot 启动类 SpringApplication 二 run方法

配置

在这里插入图片描述
Program arguments配置2个参数:--server.port=8081 --spring.profiles.active=dev

run方法

run方法执行结束代表SpringBoot启动完成,即完成加载bean。

// ConfigurableApplicationContext 是IOC容器
public ConfigurableApplicationContext run(String... args) {// 1. 启动计时Startup startup = Startup.create();if (this.registerShutdownHook) {// shutdownHook是JVM正常或者异常退出时执行的方法,此处`enableShutdownHookAddition`不是已经添加方法,只是标记可以添加shutdownHook方法。SpringApplication.shutdownHook.enableShutdownHookAddition();}// 2. 创建引导上下文DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null; // context 就是ioc容器configureHeadlessProperty();  // 允许JVM在没有显示器、鼠标、键盘等外设的情况工作,web项目也用不着外设// 3. 加载监听器SpringApplicationRunListeners listeners = getRunListeners(args);// 4. 执行监听器starting方法listeners.starting(bootstrapContext, this.mainApplicationClass);try {// args是spirngboot main方法的参数,将其封装为ApplicationArguments类ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 5. 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 打印bannerBanner printedBanner = printBanner(environment);// 创建IOC容器context = createApplicationContext();// 开始配置IOC容器context.setApplicationStartup(this.applicationStartup);prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);// 结束配置IOC容器startup.started();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);}listeners.started(context, startup.timeTakenToStarted());// 执行`Runner`方法,可以用于项目启动后删除敏感配置文件callRunners(context, applicationArguments);}catch (Throwable ex) {throw handleRunFailure(context, ex, listeners);}try {if (context.isRunning()) {listeners.ready(context, startup.ready());}}catch (Throwable ex) {throw handleRunFailure(context, ex, null);}return context;
}

1. 启动计时

org.springframework.boot.SpringApplication.Startup是计时类,用于统计启动时间。CoordinatedRestoreAtCheckpointStartup是针对CRac(Coordinated Restore at Checkpoint)项目的计时实现类。CRac项目可以对JVM状态建立快照,并且存入磁盘。之后将JVM状态从保存的检查点恢复到内存。
普通SpringBoot项目用的是StandardStartup

abstract static class Startup {private Duration timeTakenToStarted;protected abstract long startTime();protected abstract Long processUptime();protected abstract String action();final Duration started() {long now = System.currentTimeMillis();this.timeTakenToStarted = Duration.ofMillis(now - startTime());return this.timeTakenToStarted;}Duration timeTakenToStarted() {return this.timeTakenToStarted;}private Duration ready() {long now = System.currentTimeMillis();return Duration.ofMillis(now - startTime());}static Startup create() {ClassLoader classLoader = Startup.class.getClassLoader();return (ClassUtils.isPresent("jdk.crac.management.CRaCMXBean", classLoader)&& ClassUtils.isPresent("org.crac.management.CRaCMXBean", classLoader))? new CoordinatedRestoreAtCheckpointStartup() : new StandardStartup();}
}
private static final class StandardStartup extends Startup {private final Long startTime = System.currentTimeMillis(); // 加载StandardStartup类对象的时间@Overrideprotected long startTime() {return this.startTime;}@Overrideprotected Long processUptime() {try {return ManagementFactory.getRuntimeMXBean().getUptime();}catch (Throwable ex) {return null;}}@Overrideprotected String action() {return "Started";}
}

2. 创建引导上下文

创建一个上下文。用从META-INF/spring.factories文件加载的初始化器初始化引导上下文。
普通SpringBoot项目用不着这个。

private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));return bootstrapContext;
}

3. 加载监听器

这个方法从2个地方获取监听器类。一个是META-INF/spring.factories,一个是hook
SpringApplicationRunListeners对象是对监听器类的封装。

private SpringApplicationRunListeners getRunListeners(String[] args) {ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);argumentResolver = argumentResolver.and(String[].class, args);// 在`META-INF/spring.factories`中加载`SpringApplicationRunListener.class`的实现类。List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,argumentResolver);SpringApplicationHook hook = applicationHook.get();SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;if (hookListener != null) {listeners = new ArrayList<>(listeners);// 加载hook的监听类listeners.add(hookListener);}return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}

自己实现一个监听器类,并且在META-INF/spring.factories文件中加一行org.springframework.boot.SpringApplicationRunListener=com.example.demo.MyAppListener

public class MyAppListener implements SpringApplicationRunListener {@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("正在启动");SpringApplicationRunListener.super.starting(bootstrapContext);}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("环境已经准备好了");SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("上下文准备好了");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("ioc加载完成了");}@Overridepublic void started(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("启动完成");}@Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("准备就绪");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("应用启动失败");}
}

SpringBoot加载2个监听器类。其中EventPublishingRunListenerSpring自带的。
在这里插入图片描述

4. 执行监听器starting方法

SpringApplicationRunListeners 的成员变量listeners存储所有监听器。

class SpringApplicationRunListeners {private final Log log;private final List<SpringApplicationRunListener> listeners;private final ApplicationStartup applicationStartup;SpringApplicationRunListeners(Log log, List<SpringApplicationRunListener> listeners,ApplicationStartup applicationStartup) {this.log = log;this.listeners = List.copyOf(listeners);this.applicationStartup = applicationStartup;}void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});}
}private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,Consumer<StartupStep> stepAction) {// StartupStep 不是`Startup`,后者是计时器,前者表示执行阶段StartupStep step = this.applicationStartup.start(stepName);// 这个语句就是`this.listeners.forEach((listener) -> listener.starting(bootstrapContext))`,执行监听器的starting方法this.listeners.forEach(listenerAction);if (stepAction != null) {stepAction.accept(step);}step.end();}

控制台输出正在启动
在这里插入图片描述

5. 准备环境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// Create and configure the environment// environment的实际类型是ApplicationServletEnvironmentConfigurableEnvironment environment = getOrCreateEnvironment();// 1. 配置环境configureEnvironment(environment, applicationArguments.getSourceArgs());// 2. 新增`ConfigurationPropertySource`ConfigurationPropertySources.attach(environment);listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);Assert.state(!environment.containsProperty("spring.main.environment-prefix"),"Environment prefix cannot be set via properties.");bindToSpringApplication(environment);if (!this.isCustomEnvironment) {EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}

1. 配置环境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {if (this.addConversionService) {// 配置默认的类型转化器`converters`和格式化器`formatters`environment.setConversionService(new ApplicationConversionService());}configurePropertySources(environment, args);configureProfiles(environment, args);
}

configurePropertySources方法将Program arguments封装为SimpleCommandLinePropertySource
传入environment对象的propertysources
SpringApplication未实现configureProfiles方法.
在这里插入图片描述

2. 新增ConfigurationPropertySource

public static void attach(Environment environment) {Assert.isInstanceOf(ConfigurableEnvironment.class, environment);MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();PropertySource<?> attached = getAttached(sources);if (!isUsingSources(attached, sources)) {attached = new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,new SpringConfigurationPropertySources(sources));}sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);sources.addFirst(attached);
}

这个方法的目的是将environment.propertySources添加一个configurationpropertiesconfigurationproperties是对environment.propertySources的封装,目的是借助propertyresolver对象解析属性。
在这里插入图片描述

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

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

相关文章

如何调大unity软件的字体

一、解决的问题&#xff1a; unity软件的字体太小&#xff0c;怎么调大点&#xff1f;二、解决方法&#xff1a; 1.操作步骤&#xff1a; 打开Unity编辑器> Edit>preferences> UI Scaling>Use custom scaling value&#xff08;取消勾选“使用默认桌面设置”&…

SYD881X RTC定时器事件在调用timeAppClockSet后会出现比较大的延迟

RTC定时器事件在调用timeAppClockSet后会出现比较大的延迟 这里RTC做了两个定时器一个是12秒,一个是185秒: #define RTCEVT_NUM ((uint8_t) 0x02)//当前定时器事件数#define RTCEVT_12S ((uint32_t) 0x0000002)//定时器1s事件 /*整分钟定时器事件&#xff0c;因为其余的…

内置函数.

日期函数 current_date/time() 日期/时间 获得年月日&#xff1a; 获得时分秒&#xff1a; 获得时间戳&#xff1a;日期时间 now()函数 体会date(datetime)的用法&#xff1a;只显示日期 在日期的基础上加日期&#xff1a;按照日历自动计算 关键字为 intervalinterval 后的数值…

PHP 微信棋牌开发全解析:高级教程

PHP - 多维数组详解 多维数组是 PHP 中一种强大的数据结构&#xff0c;指的是一个数组的元素中可以包含一个或多个数组。它常用于存储复杂的嵌套数据&#xff0c;如表格数据或多层次关系的数据结构。 注释&#xff1a; 数组的维度表示您需要指定索引的层级数&#xff0c;以访问…

【Java】递归算法

递归的本质&#xff1a; 方法调用自身。 案例1. 斐波那契数列 1 1 2 3 5 8 13 21 .. f(n)f(n-1)f(n-2) 方法的返回值&#xff1a; 只要涉及到加减乘除&#xff0c;就是int,其他的就是void。 案例2. 青蛙跳台 青蛙一次可以跳一级台阶&#xff0c;也可以跳两级台阶&#xff…

JVM简介—1.Java内存区域

大纲 1.运行时数据区的介绍 2.运行时数据区各区域的作用 3.各个版本内存区域的变化 4.直接内存的使用和作用 5.站在线程的角度看Java内存区域 6.深入分析堆和栈的区别 7.方法的出入栈和栈上分配、逃逸分析及TLAB 8.虚拟机中的对象创建步骤 9.对象的内存布局 10.对象的…

大腾智能CAD:国产云原生三维设计新选择

在快速发展的工业设计领域&#xff0c;CAD软件已成为不可或缺的核心工具。它通过强大的建模、分析、优化等功能&#xff0c;不仅显著提升了设计效率与精度&#xff0c;还促进了设计思维的创新与拓展&#xff0c;为产品从概念构想到实体制造的全过程提供了强有力的技术支持。然而…

设计模式の享元模板代理模式

文章目录 前言一、享元模式二、模板方法模式三、代理模式3.1、静态代理3.2、JDK动态代理3.3、Cglib动态代理3.4、小结 前言 本篇是关于设计模式中享元模式、模板模式、以及代理模式的学习笔记。 一、享元模式 享元模式是一种结构型设计模式&#xff0c;目的是为了相似对象的复用…

Linux网络功能 - 服务和客户端程序CS架构和简单web服务示例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 概述准备工作扫描服务端有那些开放端口创建客户端-服务器设置启动服务器和客户端进程双向发送数据保持服务器进程处于活动状态设置最小…

用人话讲计算机:Python篇!(十五)迭代器、生成器、装饰器

一、迭代器 &#xff08;1&#xff09;定义 标准解释&#xff1a;迭代器是 Python 中实现了迭代协议的对象&#xff0c;即提供__iter__()和 __next__()方法&#xff0c;任何实现了这两个方法的对象都可以被称为迭代器。 所谓__iter__()&#xff0c;即返回迭代器自身 所谓__…

小程序快速实现大模型聊天机器人

需求分析&#xff1a; 基于大模型&#xff0c;打造一个聊天机器人&#xff1b;使用开放API快速搭建&#xff0c;例如&#xff1a;讯飞星火&#xff1b;先实现UI展示&#xff0c;在接入API。 最终实现效果如下&#xff1a; 一.聊天机器人UI部分 1. 创建微信小程序&#xff0c…

【Android】unzip aar删除冲突classes再zip

# 解压JAR文件 jar xf your-library.jar # 解压AAR文件&#xff08;AAR实际上是ZIP格式&#xff09; unzip your-library.aar # 删除不需要的类 rm -rf path/to/com/example/unwanted/ # 对于JAR打包 jar cf your-library-modified.jar -C path/to/unzipped/ . # 对于AAR打包…

使用C语言编写UDP循环接收并打印消息的程序

使用C语言编写UDP循环接收并打印消息的程序 前提条件程序概述伪代码C语言实现编译和运行C改进之自由设定端口注意事项在本文中,我们将展示如何使用C语言编写一个简单的UDP服务器程序,该程序将循环接收来自指定端口的UDP消息,并将接收到的消息打印到控制台。我们将使用POSIX套…

你的第一个博客-第一弹

使用 Flask 开发博客 Flask 是一个轻量级的 Web 框架&#xff0c;适合小型应用和学习项目。我们将通过 Flask 开发一个简单的博客系统&#xff0c;支持用户注册、登录、发布文章等功能。 步骤&#xff1a; 安装 Flask 和其他必要库&#xff1a; 在开发博客之前&#xff0c;首…

Vue(二)

1.Vue生命周期 Vue生命周期就是一个Vue实例从 创建 到 销毁 的整个过程。生命周期四个阶段&#xff1a; 创建阶段&#xff1a;创建响应式数据。 挂载阶段&#xff1a;渲染模板。 更新阶段&#xff1a;修改数据&#xff0c;更新视图。 销毁阶段&#xff1a;销毁Vue实例。 …

macOS 配置 vscode 命令行启动

打开 vscode 使用 cmd shift p 组合快捷键&#xff0c;输入 install 点击 Install ‘code’ command in PATH Ref https://code.visualstudio.com/docs/setup/mac

python coding(二) Pandas 、PIL、cv2

Pandas 一个分析结构化数据的工具集。Pandas 以 NumPy 为基础&#xff08;实现数据存储和运算&#xff09;&#xff0c;提供了专门用于数据分析的类型、方法和函数&#xff0c;对数据分析和数据挖掘提供了很好的支持&#xff1b;同时 pandas 还可以跟数据可视化工具 matplotli…

期权VIX指数构建与择时应用

芝加哥期权交易 所CBOE的波动率指数VIX 是反映 S&P 500 指数未来 30 天预测期波动率的指标&#xff0c;由于预期波动率多用于表征市场情绪&#xff0c;因此 VIX 也被称为“ 恐慌指数”。 VIX指数计算 VIX 反映了市场情绪和投资者的风险偏好&#xff0c; 对于欧美市场而言…

一区牛顿-拉夫逊算法+分解+深度学习!VMD-NRBO-Transformer-GRU多变量时间序列光伏功率预测

一区牛顿-拉夫逊算法分解深度学习&#xff01;VMD-NRBO-Transformer-GRU多变量时间序列光伏功率预测 目录 一区牛顿-拉夫逊算法分解深度学习&#xff01;VMD-NRBO-Transformer-GRU多变量时间序列光伏功率预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.中科院一区…

Elasticsearch:什么是提示工程 - prompt engineering?

提示工程流程定义 提示工程是一种工程技术&#xff0c;用于设计生成式 AI 工具&#xff08;generative AI tools&#xff09;的输入&#xff0c;以调整大型语言模型并优化输出。 提示&#xff08;prompts&#xff09;被称为输入&#xff0c;而由生成性 AI 工具生成的答案是输…