SpringBoot启动原理——Run方法源码解析《课时十二》

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: SpringBoot启动原理——Run方法源码解析《课时十二》

SpringBoot启动运行项目是运行流程如上图所示


1. ApplicationStartingEvent`在运行开始时但在任何处理之前发送,除了侦听器和初始化程序的注册。

2. `ApplicationEnvironmentPreparedEvent`当`Environment`在上下文中使用的 已知但在创建上下文之前发送一个。

3. `ApplicationContextInitializedEvent`当`ApplicationContext`准备好并调用 Application Context Initializers 但在加载任何 bean 定义之前发送一个。

4. `ApplicationPreparedEvent`在刷新开始之前但在加载 bean 定义之后发送一个。

5. An`ApplicationStartedEvent`在上下文刷新之后但在调用任何应用程序和命令行运行程序之前发送。

6. `AvailabilityChangeEvent`在 with 之后立即发送一个`LivenessState.CORRECT`,表明应用程序被认为是活动的。

7. `ApplicationReadyEvent`在调用任何[应用程序和命令行运行程序](https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-command-line-runner)后发送一个。

8. `AvailabilityChangeEvent`在 with 之后立即发送一个`ReadinessState.ACCEPTING_TRAFFIC`,表示应用程序已准备好为请求提供服务。

9. 如果`ApplicationFailedEvent`启动时出现异常,则发送一个。

结论一:

  1. 1 判断项目类型
  2. 2 推断当前主类
  3. 3 设置监听器 获取上下文 全面接管SpringMvc配置
  4. 4 找到运行主类\

结论二:

.初始化SpringApplication 从spring.factories 读取listener ApplicationContextlnitializer


运行run方法


.读取 环境变量 配置信息....


.创建springApplication上下文:ServletWebServerApplicationContext


预初始化上下文:读取启动类


.调用refresh 加载Ioc容器 加载所有的自动配置类 创建servlet容器


在这个过程中springboot会调用很多监听器对外进行扩展

package com.spring.springboot0907web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//深入源码
@SpringBootApplication
public class Springboot0907WebApplication {
    public static void main(String[] args) {
        //调用  SpringApplication.run 来启用
        SpringApplication.run(Springboot0907WebApplication.class, args);
    }
}
 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

上面的代码不做介绍重点是今天要讲的Run方法:在这个方法中他要运行很多信息:

用户进入到Run方法中如下面的图所示

 public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

对上面的源码进行解析:

1 用来记录当前springBoot启动耗时 和记录启动时间

 long startTime = System.nanoTime();
 DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();

2 他是任何spring上下文的接口 所以他可以接收任何的ApplicationContext

ConfigurableApplicationContext context = null;

3   开启了Headless模式

this.configureHeadlessProperty()

4 去SpringFactories中读取     SpringApplicationRunListeners 的组件 又是用来发布监听器的

SpringApplicationRunListeners listeners = this.getRunListeners(args);
//点击getRunListeners(args)
 private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
    }

5 发布 ApplicationStartingEvent事件 在运行时发送

listeners.starting(bootstrapContext, this.mainApplicationClass);

6  根据命令行参数 实例一个 applicationArguments  对象

   ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

7  预初始化环境:读取变量 读取配置文件信息(基于监听器)

  ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);

插入一点:prepareEnvironment 介绍

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
            environment = environmentConverter.convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

1 根据webApplicationType 创建Environment 创建 就会读取: java环境 变量和系统环境变量

ConfigurableEnvironment environment = this.getOrCreateEnvironment();

2 将命令行参数读取环境变量中

 this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());

3 将PropertieSource的配置信息放在第一 位,因 为读取配置文件@PropertieSource优先级是最低的

ConfigurationPropertySources.attach((Environment)environment);

4 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件

  listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);

5 将所有spring .main开头的配置信息绑定SpringApplication

this.bindToSpringApplication((ConfigurableEnvironment)environment);

8 忽略beaninfo 的bean

this.configureIgnoreBeanInfo(environment);

9 打印Bean横幅

Banner printedBanner = this.printBanner(environment);

10 根据 webApplicationContext创建Spring上下文

context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

11 准备应用上下文

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

12. 刷新应用上下文

this.refreshContext(context);

13 刷新完成后调          

this.afterRefresh(context, applicationArguments);

14 计算启动时间,JDK Duration类表示秒或纳秒时间间隔,

Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
//15. 打印启动日志
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
            // 16. 开始执行监听器
            listeners.started(context, timeTakenToStartup);
            // 17. 从应用上下文中获取ApplicationRunner和CommandLinerRunner类型的Bean并调用他们的run方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            // 处理异常
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }
        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            // 18. 准备监听器
            listeners.ready(context, timeTakenToReady);
            // 19返回上下文
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }

今天的Run方法的启动流程到这里结束了.结论自己总结

图一

 

图二

 

相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
3天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
12 0
|
7天前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
43 17
|
8天前
|
JSON PHP 数据格式
PHP解析配置文件的常用方法
INI文件是最常见的配置文件格式之一。
|
4天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
15天前
|
机器学习/深度学习 人工智能 安全
TPAMI:安全强化学习方法、理论与应用综述,慕工大、同济、伯克利等深度解析
【10月更文挑战第27天】强化学习(RL)在实际应用中展现出巨大潜力,但其安全性问题日益凸显。为此,安全强化学习(SRL)应运而生。近日,来自慕尼黑工业大学、同济大学和加州大学伯克利分校的研究人员在《IEEE模式分析与机器智能汇刊》上发表了一篇综述论文,系统介绍了SRL的方法、理论和应用。SRL主要面临安全性定义模糊、探索与利用平衡以及鲁棒性与可靠性等挑战。研究人员提出了基于约束、基于风险和基于监督学习等多种方法来应对这些挑战。
32 2
|
16天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
37 3
|
3天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
3天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
11 0
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
143 1

推荐镜像

更多