【Spring Boot 源码学习】SpringApplication 的 run 方法核心流程介绍

《Spring Boot 源码学习系列》

在这里插入图片描述

SpringApplication 的 run 方法核心流程介绍

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • 3.1 run 方法源码初识
    • 3.2 引导上下文 BootstrapContext
    • 3.3 系统属性【java.awt.headless】
    • 3.4 早期启动阶段
    • 3.5 准备和配置应用环境
    • 3.6 打印 Banner 信息
    • 3.7 新建应用上下文
    • 3.8 准备和配置应用上下文
    • 3.9 刷新应用上下文
    • 3.10 afterRefresh 方法
    • 3.11 打印启动日志
    • 3.12 Spring 容器启动完成
    • 3.13 callRunners 方法
    • 3.14 Spring 容器正在运行中
    • 3.15 异常处理
  • 四、总结

一、引言

在前面的博文《初识 SpringApplication》中,Huazie 带大家一起分析了 SpringApplication 类实例化的逻辑。当 SpringApplication 对象被创建之后,我们就可以调用它的 run 方法来启动和运行 Spring Boot 项目。

本篇博文将围绕 SpringApplicationrun 方法展开,带大家一起从源码分析 Spring Boot 的运行流程。

在这里插入图片描述

二、往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解
【Spring Boot 源码学习】OnWebApplicationCondition 详解
【Spring Boot 源码学习】@Conditional 条件注解
【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解
【Spring Boot 源码学习】RedisAutoConfiguration 详解
【Spring Boot 源码学习】JedisConnectionConfiguration 详解
【Spring Boot 源码学习】初识 SpringApplication
【Spring Boot 源码学习】Banner 信息打印流程
【Spring Boot 源码学习】自定义 Banner 信息打印
【Spring Boot 源码学习】BootstrapRegistryInitializer 详解
【Spring Boot 源码学习】ApplicationContextInitializer 详解
【Spring Boot 源码学习】ApplicationListener 详解
【Spring Boot 源码学习】SpringApplication 的定制化介绍
【Spring Boot 源码学习】BootstrapRegistry 详解
【Spring Boot 源码学习】深入 BootstrapContext 及其默认实现
【Spring Boot 源码学习】BootstrapRegistry 初始化器实现
【Spring Boot 源码学习】BootstrapContext的实际使用场景
【Spring Boot 源码学习】深入 ApplicationContext 初始化器实现
【Spring Boot 源码学习】共享 MetadataReaderFactory 上下文初始化器
【Spring Boot 源码学习】ConditionEvaluationReport 日志记录上下文初始化器

三、主要内容

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

3.1 run 方法源码初识

在这里插入图片描述

上述截图就是 SpringApplicationrun 方法核心代码。

下面 Huazie 将带着大家一起通读这块源码,从整体上了解下 run 方法核心流程。

3.2 引导上下文 BootstrapContext

DefaultBootstrapContext bootstrapContext = createBootstrapContext();

翻看 DefaultBootstrapContext 的源码可知,从 Spring Boot 2.4.0 版本开始支持引导上下文。

在这里插入图片描述

在《BootstrapRegistryInitializer 详解》中,Huazie 带大家详细分析了加载并初始化 BootstrapRegistryInitializer 的逻辑。而这里的 createBootstrapContext 方法就是用于创建默认的引导上下文对象 DefaultBootstrapContext,并利用 BootstrapRegistry 初始化器初始化该引导上下文对象。

想深入了解的朋友们,可查看 Huazie 下面列出的博文:

  • 《BootstrapRegistry 详解》
  • 《深入 BootstrapContext 及其默认实现》
  • 《BootstrapRegistry 初始化器实现》
  • 《BootstrapContext的实际使用场景》

3.3 系统属性【java.awt.headless】

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";// 配置 java.awt.headless 系统属性
private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

java.awt.headlessJava 中的一个系统属性,用于指示 Java 应用程序是否运行在 Headless 模式下。Headless 模式是指系统缺少显示设备、键盘或鼠标的状态,通常应用于服务器环境,如应用集群、数据库集群等,这些环境通常通过网络远程操作,没有实际的显示设备。

Java 中,AWT(Abstract Window Toolkit) 是用于构建图形用户界面(GUI)应用的标准 API 接口。

JavaAWT 提供了两种模式实现以适应不同的运行环境:

  • 标准模式,适用于具有可用显示设备、驱动和图形用户界面的环境。
  • Headless 模式 ,适用于没有显示设备、驱动或图形用户界面的环境,例如服务器。

注意: 设置 java.awt.headless 属性为 true 会使 Java AWT 工具包在 headless 模式下运行,这意味着它将不会尝试加载或访问与图形用户界面相关的资源或功能。

3.4 早期启动阶段

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

SpringApplicationRunListeners 中包含了一组 SpringApplicationRunListener 的集合。SpringApplicationRunListenerSpringApplicationrun 方法的监听器,它用来监听 Spring Boot 应用的不同启动阶段,这些阶段都会发布对应的事件。

这里 starting 方法,就对应了最早期的启动阶段,它在 run 方法刚开始执行时就被立即调用。starting 方法里会发布 ApplicationStartingEvent 事件,通过监听该事件,应用可以执行一些非常早期的初始化工作,比如配置系统属性、初始化基础组件等等。

3.5 准备和配置应用环境

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);

ApplicationArgumentsSpring Boot 中用于获取命令行参数的接口,其默认实现是 DefaultApplicationArguments

prepareEnvironment 方法用于准备和配置应用程序的运行时环境,它会发布 ApplicationEnvironmentPreparedEvent 事件,通过监听该事件,应用程序可以执行一系列操作来准备和配置其运行环境。其返回的 ConfigurableEnvironment 对象,包含了应用程序的所有配置信息。

通过 ConfigurableEnvironment 对象,我们可以获取特定配置属性的值,也可以在运行时动态修改配置属性。

我们来看看 configureIgnoreBeanInfo 方法:

在这里插入图片描述

在这里插入图片描述

configureIgnoreBeanInfo 方法中,可以看到如下代码:

Boolean ignore = environment.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,Boolean.class, Boolean.TRUE);

从上述代码中,可以看到通过 environment 变量获取属性名为 spring.beaninfo.ignore 的属性值,其 getProperty 方法有三个参数:

  • 第一个参数是属性名。
  • 第二个参数是期望返回的属性值的类型,这里是 Boolean.class
  • 第三个参数是默认值,如果找不到属性或者属性不能被转换为 Boolean 类型,则使用 Boolean.TRUE 作为默认值。

系统属性 spring.beaninfo.ignore 用于指示 Spring 在调用 JavaBeans Introspector 时使用Introspector.IGNORE_ALL_BEANINFO 模式。如果此属性的值为 true,则 Spring 会跳过搜索 BeanInfo 类(通常适用于以下情况:应用程序中的 beans 从一开始就没有定义这样的类)。

默认值是 false,表示 Spring 会考虑所有的 BeanInfo 元数据类,就像标准 Introspector.getBeanInfo(Class) 调用那样。如果在启动时或延迟加载时,反复访问不存在的 BeanInfo 类开销很大,可以考虑将此标志切换为 true

请注意:如果存在反复访问不存在的 BeanInfo 类,可能也表明缓存未奏效。最好将 Springjar 包与应用类放在同一个 ClassLoader 中,这样可以在任何情况下与应用程序的生命周期一起进行干净的缓存。对于 Web 应用程序,如果采用多 ClassLoader 布局,可以考虑在 web.xml 中声明一个本地的 org.springframework.web.util.IntrospectorCleanupListener,这也可以实现有效的缓存。

3.6 打印 Banner 信息

Banner printedBanner = printBanner(environment);

printBanner 方法用于 Spring Boot 启动时的 Banner 信息打印。

想要深入了解 Banner 打印的读者们,请查看如下博文:

  • 《Banner 信息打印流程》
  • 《自定义 Banner 信息打印》

3.7 新建应用上下文

ConfigurableApplicationContext context = createApplicationContext();protected ConfigurableApplicationContext createApplicationContext() {return this.applicationContextFactory.create(this.webApplicationType);
}

上述 createApplicationContext 方法的功能是:根据给定的 Web 应用程序类型 webApplicationType 创建一个可配置的应用上下文对象 ConfigurableApplicationContext

在《初识 SpringApplication》这篇博文的 2.2 小节Web 应用类型推断】中,大家可以看到 Web 应用程序类型 webApplicationType 是如何获取的,这里不赘述了,感兴趣的可以自行查看。

3.8 准备和配置应用上下文

context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

setApplicationStartup 方法用于设置当前应用上下文的 ApplicationStartup ,这允许应用上下文在启动期间记录指标。

prepareContext 方法用于准备和配置应用程序上下文,这里会依次发布如下事件:

  1. ApplicationContextInitializedEvent:当 SpringApplication 启动并且 ApplicationContext 已准备好,且 ApplicationContextInitializer 集合已被调用,但在加载任何 bean 定义之前,将发布该事件。
  2. ApplicationPreparedEvent :当 SpringApplication 启动并且 ApplicationContext 已经完全准备好但尚未刷新时,将发布事件。在此阶段,bean 定义将被加载,环境已经准备好可以使用。

3.9 刷新应用上下文

static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook();refreshContext(context);private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}refresh(context);
}protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();
}

registerShutdownHook 变量表示是否应注册一个关闭钩子,默认为 true

SpringApplicationShutdownHook 是一个用于执行 Spring Boot 应用程序优雅关闭的 Runnable 关机钩子。这个钩子跟踪已注册的应用程序上下文以及通过 SpringApplication.getShutdownHandlers() 注册的任何操作。

refreshContext 方法里面可以看到调用 refresh 方法,refresh 方法里面则是调用 ConfigurableApplicationContext【实现类是 AbstractApplicationContext ,该类属于 spring-context 包】的 refresh 方法,该方法是用来刷新底层的应用上下文。

它会加载或刷新配置的持久化表示,这可能来自基于 Java 的配置、XML 文件、属性文件、关系数据库模式或其他某种格式。调用此方法后,要么实例化所有单例对象,要么不实例化任何单例对象。

它最后会发布 ContextRefreshedEvent 事件,通过监听该事件,可以执行一些应用上下文初始化或刷新后需要进行的操作。

3.10 afterRefresh 方法

刷新应用上下文之后,调用 afterRefresh 方法。该方法的实现默认为空,可由开发人员自行扩展。

在这里插入图片描述

3.11 打印启动日志

long startTime = System.nanoTime();Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}

System.nanoTime() 用于获取当前时间的纳秒值。

Duration.ofNanos() 用于将纳秒数转换为 Duration 对象,timeTakenToStartup 表示 Spring Boot 应用启动所需的时间。

logStartupInfo 表示是否需要记录启动信息,如果为 true,则需要记录启动信息。

StartupInfoLogger 类用于在应用程序启动时记录应用信息,其中 logStarted 方法用于以 INFO 日志级别打印应用启动时间。

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

实际运行日志信息类似如下:

在这里插入图片描述

3.12 Spring 容器启动完成

listeners.started(context, timeTakenToStartup);

这里表示上下文已经刷新,应用程序已经启动,但是 CommandLineRunnersApplicationRunners 尚未被调用。

SpringApplicationRunListenersstarted 方法里会发布 ApplicationStartedEvent 事件,通知监听器 Spring 容器启动完成。

3.13 callRunners 方法

callRunners(context, applicationArguments);

callRunners 方法里面会调用 ApplicationRunnerCommandLineRunner 的运行方法。

在这里插入图片描述

通过阅读上述代码,可以总结如下:

  • 首先,从 context 中获取类型为 ApplicationRunnerCommandLineRunnerBean
  • 接着,将它们放入 List 列表中,并进行排序。
  • 最后,再遍历排序后的 ApplicationRunnerCommandLineRunnerBean,并调用它们的 run 方法。

Spring Boot 提供 ApplicationRunnerCommandLineRunner 这两种接口,是为了通过它们来实现在容器启动时执行一些操作。在同一个应用上下文中可以定义多个 ApplicationRunnerCommandLineRunnerbean,并可以使用 Ordered 接口或 @Order 注解进行排序。

ApplicationRunnerCommandLineRunner 这两个接口都有一个 run 方法,但不同之处是:

  • ApplicationRunnerrun 方法的参数为 ApplicationArguments
  • CommandLineRunnerrun 方法的参数为 字符串数组

如果需要访问 ApplicationArguments 而不是原始的字符串数组,大家可以考虑使用 ApplicationRunner

3.14 Spring 容器正在运行中

Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);

这里表示应用上下文已经刷新,所有的 CommandLineRunnersApplicationRunners 都已被调用,应用程序已准备好处理请求。

SpringApplicationRunListenersready 方法里会发布 ApplicationReadyEvent 事件,通知监听器 Spring 容器正在运行中。

Spring Boot 2.6.0 版本之前,大家看到调用的是 SpringApplicationRunListenerrunning 方法。从 Spring Boot 2.6.0 版本开始,新增了 ready 方法替代 running 方法。在 Spring Boot 3.0.0 版本中正式去除 running 方法。

在这里插入图片描述

3.15 异常处理

handleRunFailure(context, ex, listeners);

3.53.13 小节 ,如果出现异常,则会捕获后调用 handleRunFailure 进行异常处理。

3.14 小节,同样它如果出现异常,也会捕获后调用 handleRunFailure 进行异常处理。

handleRunFailure 方法里会发布 ApplicationFailedEvent 事件,通过监听该事件,开发人员可以实现如下的一些操作:

  • 错误日志记录:当应用启动失败时,可以记录详细的错误信息到日志文件中,便于后续的问题排查和分析。
  • 通知发送:在应用启动失败时,可以发送通知给相关的开发或运维人员,以便他们能够及时响应并处理问题。
  • 数据备份:如果应用在启动过程中出现异常,可能需要对某些关键数据进行备份,以防止数据丢失。
  • 资源清理:在应用启动失败的情况下,可能需要释放或清理已经分配的资源,如数据库连接、文件句柄等。
  • 尝试自动恢复:在某些情况下,可以尝试自动重启应用或者执行其他恢复操作,以减少人工干预的需求。
  • 自定义处理逻辑:根据具体的业务需求,实现自定义的错误处理逻辑,比如回滚事务、关闭网络连接等。

有关这块更详细的内容,后续 Huazie 将专门出一篇讲解,敬请期待!!!

四、总结

本篇 Huazie 向大家初步介绍了 SpringApplicationrun 方法核心流程。由于篇幅受限,其中很多环节并未深入讲解,后续 Huazie 将会针对这些内容深入分析,和大家一起从源码详细了解 Spring Boot 的运行流程。

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

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

相关文章

Blender表面细分的操作

在使用Blender的过程中,刚开始创建的模型,都会比较少面,这样操作起来比较流畅,减少电脑的计算量,当设计快要完成时,就会增加表面细分,这样更加圆滑,看起来更加顺眼。 比如创建一个猴头,它会默认显示如下: 从上图可以看到,有一些表面会比较大,棱角很多。 这时候你…

win11如何重新安装应用商店,怎么重装应用商店

win11系统内置了应用商店,相当于手机的应用商城,用户们想要下载软件时,就会前往应用商店搜索下载。如果我们因为误操作,删除了win11应用商店,或者是应用商店出现闪退、卡顿等问题,这个时候,最好…

MyBatis-Spring整合

引入Spring之前需要了解mybatis-spring包中的一些重要类; http://www.mybatis.org/spring/zh/index.html 什么是 MyBatis-Spring? MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 知识基础 在开始使用 MyBatis-Spring 之前&#x…

Python学习笔记23 - 目录操作

os模块操作目录相关函数 os.path模块操作目录相关函数 案例1 —— 列出指定目录下的所有.py文件 案例2 —— walk()

内网渗透-红队内网渗透工具(Viper)

红队内网渗透工具(Viper) 最近发现一款很强大的内网渗透工具Viper 接下来我给大家介绍一下具体的安装过程,这里我在kali上进行安装 (1)首先打开kali终端,切换到root用户,确认以下操作都在root用户下操作,sudo -s 安装…

【MATLAB源码-第16期】基于matlab的MSK定是同步仿真,采用gardner算法和锁相环

1、算法描述 **锁相环(PLL)** 是一种控制系统,用于将一个参考信号的相位与一个输入信号的相位同步。它在许多领域中都有应用,如通信、无线电、音频、视频和计算机系统。锁相环通常由以下几个关键组件组成: 1. **相位…

基于springboot实现医疗病历互换系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现医疗病历交互系统演示 摘要 进入21世纪,计算机技术迅速向着网络化的、集成化方向发展。传统的单机版应用软件正在逐渐退出舞台,取而代之的是支持网络、支持多种数据信息的新一代网络版应用软件,形成了信息化的社会。信息…

2024最新 PyCharm 2024.1 更新亮点看这篇就够了

2024最新 PyCharm 2024.1 更新亮点看这篇就够了 文章目录 2024最新 PyCharm 2024.1 更新亮点看这篇就够了🚀 PyCharm 2024.1 发布:全面升级,助力高效编程!摘要引言 🚀 快速掌握 Hugging Face:模型与数据集文…

(八)C++自制植物大战僵尸游戏植物基类讲解

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/m0EtD 在植物大战僵尸游戏中,最重要的两个类别就是植物与僵尸。植物可以对僵尸进行攻击,不同的植物攻击方式千差万别,但是不同植物又有许多相同的属性。在基类(父类&#xf…

【Canvas技法】绘制正三角形、切角正三角形、圆角正三角形

【图例】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>绘制正三角形、切角正三角形、圆角正三角形</title><style …

自己操作逆向案例一——某竞赛网登录密码加密,超级简单,泪目了!

网址&#xff1a;aHR0cHM6Ly9leGFtem9uZS5zYWlrci5jb20vcXVlc3Rpb24vZXhwbG9yZQ 打开开发者工具&#xff0c;点击账号密码登录&#xff0c;进行抓包 先进行搜索&#xff0c;发现一下子就找到了&#xff0c;且看上去很像MD5加密&#xff0c;打上断点&#xff0c;再次点击登录。…

linux 自定义命令/别名

参考资料 Linux(Ubuntu)自定义命令的使用Linux/Ubuntu系统自定义Shell命令Ubuntu/Linux 操作系统 自定义命令 目录 一. 为路径取别名二. 修改.profile文件2.1 .profile简介2.2 需求2.3 修改.profile文件 三. 创建软链接 一. 为路径取别名 ⏹需求&#xff1a;有一个work文件夹…

NVM的安装与配置

目录 一、简介二、下载2.1、windows环境下载地址2.2、安装 三、配置3.1、查看可安装版本3.2、安装版本3.3、使用和切换版本3.4、模块配置 四、其他4.1、全局安装pnpm4.2、常用nvm命令 一、简介 NVM&#xff0c;全称为Node Version Manager&#xff0c;是一个流行的命令行工具&a…

第41篇:有限状态机<四>

Q&#xff1a;本期我们介绍有限状态机的应用之二&#xff1a;米里状态机“1101”序列检测器。 A&#xff1a;摩尔状态机1101序列检测器有5个状态&#xff0c;而米里状态机只有4个状态。当状态为s_3且输入为1时&#xff0c;状态机输出1。这里输出与输入一起被标志在状态转移箭头…

vue3 动态class和style

1、需求&#xff1a;一个删除的弹窗&#xff0c;点击会提示“是否需要删除XXXXX&#xff08;name&#xff09;”&#xff0c;但是name不固定&#xff0c;所以删除弹窗的width不能写死。&#xff08;如果不设置width&#xff0c;本项目的弹窗会自适应变得特别长&#xff09;

mybatis自制插件+注解实现数据脱敏

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 mybatis自制插件注解实现数据脱敏 前言数据脱敏的实现方式构思从哪个地方进行脱敏&#xff1f;它怎么知道我什么数据需要脱敏 项目实现拦截器实现注解实现枚举实现效果图展示 前言 在数字时代&#x…

hive了解系列一

“ 随着智能手机的普及&#xff0c;互联网时代红利的爆发&#xff0c;用户数量和产生的数据也越发庞大。为了解决这个问题&#xff0c;提高数据的使用价值。 Hadoop生态系统就被广泛得到应用。 在早期&#xff0c;Hadoop生态系统就是为处理如此大数据集而产生的一个合乎成本效益…

力扣第20题有效的括号

typedef char STDataType; //动态栈 #define allocator_may_return_null 1typedef struct ST {STDataType* _a;int _top;//栈顶元素int _capacity;//最大容量 }Stack; //初始化栈 void StackInit(Stack *pst);//入栈 void StackPush(Stack* pst, STDataType x);//出栈 void Sta…

小程序变更主体需要多久?

小程序迁移变更主体有什么作用&#xff1f;小程序迁移变更主体的好处有很多哦&#xff01;比如可以获得更多权限功能、公司变更或注销时可以保证账号的正常使用、收购账号后可以改变归属权或使用权等等。小程序迁移变更主体的条件有哪些&#xff1f;1、新主体必须是企业主体&am…

每日OJ题_BFS解决最短路①_力扣1926. 迷宫中离入口最近的出口

目录 力扣1926. 迷宫中离入口最近的出口 解析代码 力扣1926. 迷宫中离入口最近的出口 1926. 迷宫中离入口最近的出口 难度 中等 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 . 表示&#xff09;和墙&…