Spring Boot(二)SpringBoot是如何启动Spring容器源码

2023-10-26

SpringApplication run

调用SpringApplication.run启动springboot应用

1 SpringApplication.run(Application.class, args);

2:使用自定义SpringApplication进行启动

public static ConfigurableApplicationContext run(Class<?>[]  primarySources, String[] args) { 
      return new SpringApplication(primarySources).run(args); 
 }

创建SpringApplication new SpringApplication(primarySources)

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//将启动类放入primarySources
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 根据main方法推算出mainApplicationClass
		this.mainApplicationClass = deduceMainApplicationClass();
	}
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

org.springframework.context.ApplicationContextInitializer
在这里插入图片描述
org.springframework.context.ApplicationListener
在这里插入图片描述

总结:1. 获取启动类:根据启动类加载ioc容器2.获取web应用类型
3.spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)
4. 根据main推算出所在的类
就是去初始化了一些信息

SpringBoot 事件监听器发布顺序

1.ApplicationStartingEvent在运行开始时发送,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送。
2.在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。
3.准备ApplicationContext并调用ApplicationContextInitializers之后,将发送ApplicationContextInitializedEvent。
4.读取完配置类后发送ApplicationPreparedEvent。
5.在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将发送ApplicationStartedEvent。
6.紧随其后发送带有LivenessState.CORRECT的AvailabilityChangeEvent,以指示该应用程序被视为处于活动状态。
7.在调用任何应用程序和命令行运行程序之后,将发送ApplicationReadyEvent。
8.紧随其后发送ReadabilityState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序已准备就绪,可以处理请求。
如果启动时发生异常,则发送ApplicationFailedEvent。

1.ApplicationStartingEvent
在这里插入图片描述
2.ApplicationEnvironmentPreparedEvent
在这里插入图片描述

run方法

这个方法是启动springboot最核心的逻辑

public ConfigurableApplicationContext run(String... args) {
    // 用来记录当前springboot启动耗时
   StopWatch stopWatch = new StopWatch();
   // 就是记录了启动开始时间
   stopWatch.start();
   // 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   // 开启了Headless模式:
   configureHeadlessProperty();
   // 去spring.factroies中读取了SpringApplicationRunListener 的组件,  就是用来发布事件或者运行监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 发布1.ApplicationStartingEvent事件,在运行开始时发送
   listeners.starting();
   try {
       // 根据命令行参数 实例化一个ApplicationArguments 
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 忽略beaninfo的bean
      configureIgnoreBeanInfo(environment);
      // 打印Banner 横幅
      Banner printedBanner = printBanner(environment);
      // 根据webApplicationType创建Spring上下文  
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //预初始化spring上下文
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 加载spring ioc 容器  相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
      //  加载自动配置类:invokeBeanFactoryPostProcessors ,  创建servlet容器onRefresh
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

准备环境 prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 根据webApplicationType 创建Environment  创建就会读取: java环境变量和系统环境变量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 将命令行参数读取环境变量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
   ConfigurationPropertySources.attach(environment);
   // 发布了ApplicationEnvironmentPreparedEvent 的监听器  读取了全局配置文件
   listeners.environmentPrepared(environment);
   // 将所有spring.main 开头的配置信息绑定SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //更新PropertySources
   ConfigurationPropertySources.attach(environment);
   return environment;
}

预初始化上下文 prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   // 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
   applyInitializers(context);
   // 发布了ApplicationContextInitializedEvent
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 获取当前spring上下文beanFactory (负责创建bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   // 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
   // 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // 设置当前spring容器是不是要将所有的bean设置为懒加载
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   // 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
   load(context, sources.toArray(new Object[0]));
   //4.读取完配置类后发送ApplicationPreparedEvent。
   listeners.contextLoaded(context);
}

刷新 调用ServletWebServerApplicationContext refresh 之后就会调用AbstractApplicationContext的refresh方法

	private void refreshContext(ConfigurableApplicationContext context) {
		refresh((ApplicationContext) context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

总结: 1. 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息…
4. 创建springApplication上下文:ServletWebServerApplicationContext
5. 预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
加载所有的自动配置类
创建servlet容器

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot(二)SpringBoot是如何启动Spring容器源码 的相关文章

  • Jersey 2 - ContainerRequestFilter get 方法注解

    我试图获取 ContainerRequestFilter 对象中的方法注释 控制器 GET RolesAllowed ADMIN public String message return Hello rest12 容器请求过滤器 Provi
  • 使用 IcyStreamMeta 从 SHOUTcast 获取元数据

    我正在为 Android 编写一个应用程序 从 SHOUTcast mp3 流中获取元数据 我正在使用我在网上找到的一个非常漂亮的类 我稍微修改了一下 但我仍然有两个问题 1 我必须使用 TimerTask 不断 ping 服务器来更新元数
  • Spring中需要多个相同类型的bean

    将其标记为重复之前的请求 我浏览了论坛 但在任何地方都找不到该问题的解决方案 我正在使用 Spring 3 2 编写代码 一切都是纯粹基于注释的 该代码接收从不同 XSD 文件派生的 XML 文件 所以我们可以说 有五个不同的 XSD A1
  • 如何用Spring进行只读和读写的数据库路由

    我正在研究 Spring 中的事务路由 但我的应用程序存在运行时问题 我有两个 MySQL 数据库 一个用于读取 一个用于读 写 但是我的路由配置不起作用 当我应用只读配置时 我没有成功 这是我的配置 pom xml
  • 通过 jdbc 执行存储过程时获取网关超时

    我正在使用 struts2 框架 它基本上是这样的 ActionClass execute call function in business class which returns an object and store this obj
  • 如何向正在运行的 Linux 进程发送 Ctrl-Break?

    我正在调试在 Sun 的 JDK 1 4 2 18 上运行的应用程序中的内存泄漏 该版本似乎支持命令行参数 XX HeapDumpOnCtrlBreak 这可能会导致 JVM 在遇到控制中断时转储堆 如何将其发送到 Linux 机器上的后台
  • TreeSet 给出不正确的输出 - Java8

    在处理树集时 我发现了非常奇怪的行为 根据我的理解 以下程序应该打印两行相同的行 public class TestSet static void test String args Set
  • 递归 - 与 Java 中不重复的数组相结合

    所以我知道如何获取组合的大小 数组大小 在我的例子中 除以所需数组子集大小的阶乘 我遇到的问题是获取组合 到目前为止 我已经阅读了 stackoverflow 上的大部分问题 但一无所获 我认为我发现的问题是我想将创建的组合子集中的元素添加
  • 配置 logback 以遵循 Java 配置,即 Logback 的纯 Java 配置

    我只是不喜欢 Logback 的 XML 或 Groovy 配置 更喜欢用 Java 进行配置 这也是因为我将在初始化后的不同时间在运行时更改配置 似乎对 Logback 进行 Java 配置的唯一方法是进行某种初始化劫持根追加器 http
  • Android Studio 1.0.1 APK META-INF/DEPENDENCIES 中复制的重复文件

    我安装了 Android Studio 版本 1 0 1 并尝试将我的项目从 eclipse 导入到它 它给了我以下错误 Error Execution failed for task app packageDebug Duplicate
  • Spring数据异常处理

    我正在使用 Spring Data JPA 开发一个项目 我需要处理 JpaRepository 方法调用中的一些异常 在下面的代码中 我需要拦截主键违规错误 但无法直接捕获异常 就我而言 当发生此类异常时 存储库层 JpaReposito
  • 将 JSON 与嵌套数组和 json 进行比较(数组顺序无关紧要)

    你好 我正在尝试比较java中的两个json 每个键可以包含一个json对象或json对象数组 并且它们中的每个也可以是数组或json 这是 Json 的示例 id 123123asd123 attributes name apps val
  • 动态添加组件到 JDialog

    当用户单击 JDialog 上的按钮时 我在将组件添加到 JDialog 时遇到问题 基本上我希望它看起来像这样 然后 当用户单击 添加新字段 时 我希望它看起来像这样 我似乎无法打开添加新 JLabel 或 JTextField 的对话框
  • 正确使用Optional.ifPresent()

    我正在尝试理解ifPresent 的方法OptionalJava 8 中的 API 我有一个简单的逻辑 Optional
  • 有丰富的领域模型示例吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个简单的示例来说明使用富域模型的好处 理想情况下 我想要一个之前和之后的代码列表 应该尽可能
  • 在 ant 脚本中包含外部 JAR 时出错

    这是我第一次尝试编写 ANT 脚本 这是我使用 Spring 构建的简单 Hello World 应用程序的 build xml
  • 在可序列化 Java 类中使用记录器的正确方法是什么?

    我有以下 doctored 我正在开发的系统中的类以及Findbugs http findbugs sourceforge net 正在生成一个SE BAD FIELD http findbugs sourceforge net bugDe
  • Spring MVC - 两次提供内容

    我已经花了一周时间寻找有关如何将内容服务器到我的网页的指导 两次 因为使用 Model 或 ModelAndView 切断内容一次可以工作 但如果用户再次与页面交互 我希望它加载更多内容同一页 Java Spring 后端方法 Get 有效
  • 如何在pdf中导出一对一的JTable[重复]

    这个问题在这里已经有答案了 可能的重复 为什么 JTable 标题没有出现在图像中 https stackoverflow com questions 7369814 why does the jtable header not appea
  • 如何将钱兑换成零钱

    尝试将输入的数字转换为 25 美分 50 美分 10 美分和 10 分 有几个问题 public class Coins public static void main String args private int quarters di

随机推荐

  • python类基本语法笔记

    语言是工具 一段时间不用就会忘掉语法 静态方法和类方法 什么时候会用到这样的方法呢 类方法是针对类存在的 可以用类直接调用 主要用到的两个函数是staticmethod 和classmethod 简洁的用法是用Python的修饰器 需要注意
  • Vue总结第二天~自定义子组件、父子组件通信、插槽

    目录 一 组件 组件目录 1 注册组件 全局组件 局部组件和demo template模块 1 注册组件的基本步骤 2 全局组件demo 3 局部组件demo 4 template模块的简化 模板的分离写法 即将其内容封装到 templat
  • Matplotlib

    1 折线图 import matplotlib pyplot as plt import numpy as np x np linspace 1 1 50 1到1 有五十个点 y 2 x 1 plt figure num 1 figsize
  • 【计算机网络】第一章:计算机网络概述

    文章目录 1 1 计算机网络在信息时代的作用 1 2 因特网概述 1 3 三种交换方式 1 4 计算机网络的定义和分类 1 5 计算机网络的性能指标 1 6 计算机网络体系结构 计算机网络体系结构 计算机网络体系结构分层的必要性 计算机网络
  • 从gitHub当中更新项目synchronize Update fetch pull 项目的区别。

    11 从gitHub更新项目 方法一 右击你的项目 team synchronize workspace 这样他就会去gitHub那fetch回最新的版本 之后像svn一样 切换到team synchronize视图 注意服务器如有更新 而
  • Vue之插件的介绍

    简介 主要介绍Vue插件的概念 定义和使用 Vue的插件主要是用于增强功能 可以把它看作是一个工具库 可以提供很多强大的功能 比如一些强大的自定义指令 一些强大的工具方法 过滤器等 我们可以编写或者直接引入别人写的插件 就能获得强大的功能
  • odoo 权限

    创建安全组并分配用户 Odoo中的访问权限通过安全组成进行配置 给组指定权限 然后为组分配用户 每个功能区都有中枢应用所提供的基础安全组 在插件继承已有应用时 它们应对相应的组添加权限 参见本章稍后的向模型添加访问权限一节 在插件模块添中添
  • HDOJ 1058 Humble Numbers解题报告【DP】

    Humble Numbers 题目详见http acm hdu edu cn showproblem php pid 1058 开始拿到这个题目的时候还纠结了半天 英语很差的话这个题是不可能AC的 而我就是其中之一 Humber Numbe
  • spring-boot-maven-plugin报错的修改与版本号查看

    我报错的原因是因为没加版本号 版本号是多少 可以下个everything搜spring boot maven plugin 前面的号码就是版本号了
  • [转]出租车轨迹处理(二):时空分析

    接下来就要进行一些简单的分析了 今天的目标是如何对某一感兴趣区域进行出租车数据的时空分析 一 轨迹数据预处理 这一步在上一篇文章中已经有了介绍 步骤无非就是 1 使用pandas读取数据 import pandas as pd import
  • Matlab实现粒子群算法(附上完整仿真代码)

    粒子群算法 Particle Swarm Optimization PSO 是一种群体智能算法 通过模拟自然界中鸟群 鱼群等生物群体的行为 来解决优化问题 在PSO算法中 每个个体被称为粒子 每个粒子的位置表示解空间中的一个解 每个粒子的速
  • AVL树的插入与删除(均为递归实现)

    一 引言 AVL树是带有平衡条件的二叉查找树 这个平衡条件必须要容易保持 而且它必须保证树的深度是O logN 一颗AVL树是其每个节点的左子树和右子树的高度最多差一的二叉查找树 主要介绍插入算法和删除算法 二 AVL树的结点定义 type
  • (二)RK3566 Android11固件烧录

    上一篇 一 RK3566 Android11 系统编译 文章目录 1 固件包烧录步骤 2 固件统一打包 3 固件升级 1 固件包烧录步骤 烧录工具位置 RKTools windows AndroidTool AndroidTool Rele
  • e17 enlightenment 介绍及配置

    为什么要有一个窗口管理器 为什么一定要有一个桌面背景 甚至是标题栏 或是如果把一个应用程序如firefox当成桌面背景行不行 桌面能不能再快一点 我不想把资源浪费在那些用不到的地方 Linux那么多虚拟桌面 为什么我不能在一个桌面全屏运行一
  • python django框架ORM模型及ORM操作数据库 笔记

    ORM模型介绍 随着项目的越来越大 采用写原生SQL的方式在代码中会出现大量的SQL语句 那么问题就出现了 1 SQL语句重复利用率不高 越复杂的SQL语句条件越多 代码越长 会出现很多相近的SQL语句 2 很多SQL语句是在业务逻辑中拼出
  • 深度卷积神经网络中的patch

    转载 https blog csdn net wills798 article details 97974617 在阅读基于深度卷积神经网络的图像识别 分类或检测的文献时经常看到 patch 不是很能理解 后来就总结了一下 通过阅读 pat
  • 【深度学习——点云】PointNet++

    这篇文章发表于NIPS 2017 是在PointNet基础上的工作 论文地址 PointNet Deep Hierarchical Feature Learning on Point Sets in a Metric Space 1 Mot
  • vue实现简单轮播图

    实现思路 将vue的框架封装在function中 在界面刷新时调用 将要轮播的图片存放在data中 还有下面的列表也分别保存在data中的一个数组中 然后每隔一段时间进行自动切换的函数写在methods中 注意函数要调用的话 就要在生命周期
  • 如何fork GitHub上的官方仓库

    在GitHub中 fork表示复制一个仓库到你自己的GitHub账号下 创建一个独立的副本 通过fork操作 你可以在自己的副本中进行修改 改进和实验 而不会影响到原始仓库或其他人的工作 当你fork一个仓库时 GitHub将会为你创建一个
  • Spring Boot(二)SpringBoot是如何启动Spring容器源码

    SpringApplication run 调用SpringApplication run启动springboot应用 1 SpringApplication run Application class args 2 使用自定义Spring