深入理解SpringApplication.run(PeaApplication.class,args)(1)

2023-05-16

运行流程:

在这里插入图片描述

前言

本篇将对SpringApplication.run()方法进行源码溯源,深入理解该方法

在进入该方法后,把第一个class参数转化为数组类型,调用同名方法

这里有2个核心1个是SpringApplication的创建,另1个是其run方法的调用

其中SpringApplication的创建时,进入SpringApplication的构造函数

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
 
 
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//设置资源加载器为null
		this.resourceLoader = resourceLoader;
		//断言加载资源类不能为null
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//将primarySources数组转换为List,最后放到LinkedHashSet集合中
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
 
		//【1.1 推断应用类型,后面会根据类型初始化对应的环境。常用的一般都是servlet环境 】
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//【1.2 初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer 设置初始化器】
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//【1.3 初始化classpath下所有已配置的 ApplicationListener 设置监听器 】
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//【1.4 根据调用栈,推断出 main 方法的类名 】
		this.mainApplicationClass = deduceMainApplicationClass();
	}

其中初始化与监听器 getSpringFactoriesInstances

        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

在 setInitializers() 里可以看到 有一个 getSpringFactoriesInstances()

	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
        // 根据type 从META-INF/spring.factories获取 具体的实现类字符串列表
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 实例化具体的实现类
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

 getSpringFactoriesInstances方法的第1行代码

 在传入的ResourceLoader是null时,调用ClassUtils.getDefaultClassLoader()方法

 其实currentThread() 只是Thread 的一个静态方法。返回的正是执行当前代码指令的线程引用:

        Thread.currentThread() 返回的是一个实例。 只不过这个实例确实比较特殊。 这个实例是当前Thread 的引用

        Thread.currentThread().getContextClassLoader()和Class.getClassLoader的区别

前者是最安全的方法

        如果你使用Test.class.getClassLoader(),可能会导制和当前线程所运行的类加载器不一致。(因为Java天生的多线程)

        Test.class.getClassLoader一般用在getResource,因为资源文件的位置相对是固定的。

理解线程上下文类加载器

        ThreadContextClassLoader,下文使用TCCL表示。

        Java提供了很多服务提供者接口(Service Provider Interface, SPI),允许第三方为这些接口提供实现。常见的SPI有JDBC、JCE、JNDI、JAXP和JBI等。

        这些SPI的接口由Java核心库来提供,而这些SPI的实现代码则作为Java应用所依赖的jar包被包含进classpath里。SPI接口中的代码经常需要加载具体的实现类,那么问题来了,SPI的接口是Java核心库的一部分,是由启动类加载器(Bootstrap Classloader)来加载的,SPI的实现类是由系统类加载器(System ClassLoader)来加载的。引导类加载器是无法找到SPI的实现类的,因为依照双亲委派模型,BootrapClassLoader无法委派AppClassLoader来加载类。

        而线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使用程序可以逆向使用类加载器

Thread.currentThread().getContextClassLoader()和Class.getClassLoader的区别_崔世勋的博客-CSDN博客

 

 这个方法就是查找类加载器

Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

SpringFactoriesLoader.loadFactoryNames(type, classLoader)  

这个type我们知道他是 ApplicationContextInitializer类

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//        拿到org.springframework.context.ApplicationContextInitializer
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {

//看这个意思从cache中先获取当前的类加载器加载过的Map这个类型的Map跟我们平时见到的不太一样,。他是可以一个key对应多个value的
        Map<String, List<String>> result = (Map)cache.get(classLoader);

//      如果不为空直接返回
        if (result != null) {
            return result;
        }else {
//        为空继续往下走

            HashMap result = new HashMap();

            try {

//            spring.factories里面有着自动配置(AutoConfiguration)相关的类名
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");

//大体意思就是循环上面找到的文件,读取文件里面的配置信息放到map中加入缓存

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

        可以看得出,loadSpringFactorie方法,会从META-INF/spring.factories文件中读取配置,将其封装为Properties对象,将每个key作为返回的map的key,将key对应的配置集合作为该map的value。

        而loadFactoryNames则是取出key为EnableAutoConfiguration.class的配置集合

        我们查看META-INF/spring.factories的内容(完整路径:org\springframework\boot\spring-boot-autoconfigure\2.1.4.RELEASE\spring-boot-autoconfigure-2.1.4.RELEASE.jar!\META-INF\spring.factories)

        可以看到,EnableAutoConfiguration对应的value,则是我们在开发中经常用到的组件,比如Rabbit、Elasticsearch与Redis等中间件。

        现有7个需要初始化的类

然后通过反射实例化这些instances.开始初始化工厂


	@SuppressWarnings("unchecked")
	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

Class<?> instanceClass = ClassUtils.forName(name, classLoader);  

        循环names就是上面的7个配置文件的类

最后通过


AnnotationAwareOrderComparator 进行排序  

最终完成init第一步的初始化,如图开头标题的那张图流程一样

 

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

深入理解SpringApplication.run(PeaApplication.class,args)(1) 的相关文章

  • 添加和减去额外的类

    我试图使用java脚本添加和减去一个类我也使用多个类这是我的代码 function enlargegreen document getElementByClassName playlist player className enlarge
  • 如何手动删除类的实例?

    如何手动删除类的实例 Example include
  • 如何使用下面定义的类?

    class A public B b class B public A a 我不能在 A 类 B b 中写入 因为 B 类定义如下 有什么办法让它发挥作用吗 thanks 这不可能 您需要在其中一个类中使用指针或引用 class B for
  • Fortran 03/08(gfortran 编译器)中使用无限多态类型进行数组操作

    我想通过以下方式实现有用的数组操作 添加元素 删除元素 通过可分配 指针 二叉树结构实现不同的实现 class 特征 无限多态性 我使用 gfortran 5 0 应该可以处理这样的功能 我需要它 以免为我使用的每种类型重复相同的代码 这应
  • VB6 类有析构函数吗?

    当我执行诸如以下的语句时 Set MyObject Nothing 类中是否有一个被调用的特定函数 即我可以用作析构函数 来执行诸如清理数组 与数据库断开连接等操作 类似于Class Initialize 构造函数 还有一个析构函数 Sub
  • 为什么 Swift 中委托方法需要公开?

    我正在快速开发一个框架 我正在创建一个处理框架中 BLE 内容的类 这个类应该是公共的 因为我需要从使用我的框架的外部应用程序访问这个类 我的班级结构如下 public class MyClass NSObject CBCentralMan
  • 在 Javascript 中获取类的所有实例

    我以为这个问题已经有了答案 但我似乎找不到答案 如何在 Javascript 中的此类的所有实例上运行特定的类方法 这必须在我不知道实例名称的情况下完成 我想我可以在类中使用某种静态变量来存储所有实例 但这在 JS 中似乎不存在 那么如何在
  • 从字典返回类属性的数组

    Background 最近试图回答一个question https stackoverflow com q 58835358 9758194 我自己在想是否可以直接从字典项返回类对象属性的数组 Code Imagine TstClass作为
  • 我应该将 PHP include/require 语句放在 HTML 中的什么位置?

    我在文件中创建了一个 PHP 类 现在我想在 HTML 页面中使用它 我应该把它放在哪里require or includeHTML 页面内的声明 有没有表现在 HTML 的开头或结尾加载它之间的区别 还是根本没有区别 应该是在 PHP 脚
  • Python 中的类位于不同的文件中吗?

    与 Java 或 php 非常相似 我习惯将类与文件分开 Python 中也是同样的情况吗 另外 我应该如何命名该文件 像classname py一样小写还是像ClassName py一样 如果我想从此类创建一个对象 我是否需要做一些特殊的
  • 向元素添加类

    像这样 我有两个选项卡 所以当我单击其中一个选项卡时 它是活动的 逻辑的 现在我试图区分活动选项卡和非活动选项卡 但不是使用 css 属性 但我想向单击的选项卡添加特定的类 如下所示 tab1 addClass active 但是 没有什么
  • Python 检查 __init__ 参数

    在过去的几个小时里我一直在试图解决这个问题 但我即将放弃 如何确保在 python 中只有匹配的特定条件才会创建对象 例如 假设我想创建一个对象 Hand 并且仅当初始化程序中有足够的 Fingers 时才初始化 Hand 请以此作为类比
  • 隐藏/显示切换具有相同类名的单独 div

    我有一个 UL 列表 每个 LI 都有一个隐藏的 DIV 以及一个显示隐藏 DIV 的 更多信息 链接 但是 单击此按钮也会显示所有其他 LI 的隐藏 DIV 我怎样才能只隐藏 显示 LI 中的 DIV 而不显示所有其他隐藏的 DIV 如果
  • 通过使用 Foo() 参数运行子命令,在主单击组命令上实例化 Foo() 类

    我想运行一个带有可变参数的 click 子命令 这些参数将用于在 main 组命令上实例化类 Foo args 以便创建一个由其子命令使用的 Foo 实例 以便它与点击的工作方式一致 python foo py subcommand arg
  • 封装的闭包与类?

    我是 JS 来自 C etc 的新手 我突然想到闭包似乎是比类更简单 更方便的处理封装的方法 这段代码似乎给出了一种处理封装的简单方法 function addProperty o var value o get function retu
  • 如何使用方法更改类属性?

    我有一个 Django 模型类 它有一些默认属性 我想更改complete通过调用函数来改变变量 class Foo complete False def set complete true complete True 但打电话后set c
  • 类运算符

    我在编写代码时遇到问题 void main Matrix c rows cols rows cols are int numbers c 0 0 2 the line that I m having a problem to do the
  • 计算一个类的实例?

    我一直在清理我正在扩展的模块中的一些代码 但我似乎找不到 Pythonify 这段代码的方法 global next id 1 class Obj def init self global global next id self id gl
  • Haskell 类型系统的细微差别

    我一直在深入了解 haskell 类型系统的本质 并试图了解类型类的要点 我已经学到了很多东西 但我在下面的代码片段上遇到了困难 使用这些类和实例定义 class Show a gt C a where f Int gt a instanc
  • 类、模块、它们的特征类和方法查找

    我们来开公开课吧Module并向其中添加一个方法 class Module def foo puts phew end end 我可以通过这样做来调用这个方法 Class foo 这是可以理解的 因为类Class is Class 其超类是

随机推荐

  • 本地图片转为网络链接(URL/HTML/Markdown/BBCode...)

    你还在为将图片变为链接而烦恼吗 xff1f 不要慌 xff0c 看这里https www superbed cn signup from id 61 27240 聚合图床你值得拥有 xff01 没有广告 xff0c 没有收费机制 xff01
  • 基于RS485通信的Modbus通信协议

    通信可以分为两个方面 xff1a 硬件层 xff1a RS485解决的是数据传输问题 xff0c 也就是说如何将一个 0 或 1 传输到另外一端 xff08 保证了数据可以转移到另一端 xff09 软件层 xff1a modbus是在硬件基
  • Mysql入门——初识数据库

    初识数据库 DBMS的种类 DBMS 主要通过数据的保存格式 xff08 数据库的种类 xff09 来进行分类 xff0c 现阶段主要有以下 5 种类型 层次数据库 xff08 Hierarchical Database xff0c HDB
  • 【HTTP协议】---HTTP协议详解

    HTTP协议详解 一 HTTP简介 1 HTTP协议 xff0c 即超文本传输协议 Hypertext transfer protocol 是一种详细规定了浏览器和万维网 WWW 61 World Wide Web 服务器 之间互相通信的规
  • Windows将多个TXT文档合并

    1 终端进入多个文档目录 type txt gt gt D hebing1 txt 复制这段命令到终端
  • Linux系统编程之--文件操作

    前言 xff1a Linux系统之下皆文件 1 缓冲区文件操作 普通文件 xff08 TXT MP3 MP4 xff09 缓冲区 xff1a 文件的读写并不是直接操作文件的 xff0c 而是操作缓冲区的 xff08 RAM xff09 例如
  • Linux interfaces配置静态ip(开机自启动)

    编辑 xff1a vim etc network interfaces 基本配置如下 xff1a eth0会在系统启动时被自动配置 auto eth0 eth0接口具有一个静态 xff08 static xff09 IP配置 iface e
  • 超详细Windows10 Tomcat 9安装与配置

    文章目录 附Eclipse配置Tomcat教程一 Tomcat下载二 配置环境变量三 启动Tomcat四 测试Tomcat是否启动成功 附Eclipse配置Tomcat教程 超详细Ecilpse配置Tomcat教程 一 Tomcat下载 首
  • VS2019左侧 黄、绿线条 以及 错误波浪线 隐藏

    VS2019左侧黄线 绿线 选项 gt 文本编译器 gt 常规 gt 跟踪更改 xff08 T xff09 选中后在代码左侧会出现黄绿线条 错误波浪线 选项 gt 文本编译器 gt 常规 gt 显示错误波形曲线 xff08 E xff09
  • Zemax光学设计(十)——变焦镜头设计

    变焦镜头原理 在基础光学理论中像面大小 视场和焦距三者有 如下关系 xff1a 变焦镜头的变焦倍数为长焦距和短焦距比值 xff0c 也称为倍率 在变焦过程中镜头的相对孔径保持不变 xff0c 但对于实际的高变倍比系统 xff0c 由于外形尺
  • Spring Boot注解的运行原理

    Spring Boot 是一个基于 Spring Framework 的开源框架 xff0c 通过简化配置和开发过程 xff0c 使 Spring 应用程序的开发变得更加快速和便捷 在 Spring Boot 中 xff0c 注解是非常重要
  • 在Ubuntu 18.04上安装Git

    步骤1 首先 xff0c 通过运行以下命令确保您的系统和apt包列表完全更新 xff1a apt get update y apt get upgrade y 第2步 在Ubuntu 18 04上安装Git 现在让我们安装git xff1a
  • python中‘‘‘ ‘‘‘ 的作用

    在python中 39 有下面两个作用 xff1a 1 保留原样输出 代码块就是 email message span class token operator 61 span span class token triple quoted
  • 1.4 配置通过FTP进行文件操作

  • Python编程——列表输入字典再转为DataFrame

    导入依赖库 import pandas as pd 设置两个列表 alpha list 61 1 2 3 4 5 beta list 61 2 3 4 5 6 some dict 61 设置空字典 some dict 39 a 39 61
  • 【毕设】基于SpringBoot的医院管理系统的设计和实现

    毕设 基于SpringBoot的医院管理系统的设计和实现 xff1a 本次毕设是基于SpringBoot的前后端分离医院管理系统 xff0c 毕设中包括开题任务书 开题报告 项目系统 毕设论文 相应的答辩PPT 项目演示基本业务操作流程 毕
  • Android S GTS 常见的 fail 项

    此文章只是一篇总结 xff0c 针对 MTK 平台近期的 GTS 做个简单的整理回顾 xff0c 后期不断扩展 GTS GtsJniUncompressHostTestCases com google android gts jniunco
  • 正则表达式底层实现 matcher.find

    matcher find 完成的任务 xff08 考虑分组 xff09 什么是分组 xff0c 比如 d d d d 正则表达式中有 表示分组 第1个 表示第1组 第2个 表示第2组 1 根据指定的规则 定位满足规则的子字符串 比如 19
  • MyBatisPlus配置与实现

    目录 基于SpringBoot使用MyBatisPlus 标准数据层开发 Lombok 分页功能 DQL编程控制 构建条件查询 null判定 查询投影 聚合查询 分组查询 查询条件 模糊查询 排序查询 映射匹配兼容性 DML编程控制 id生
  • 深入理解SpringApplication.run(PeaApplication.class,args)(1)

    运行流程 xff1a 前言 本篇将对SpringApplication run xff09 方法进行源码溯源 xff0c 深入理解该方法 在进入该方法后 xff0c 把第一个class参数转化为数组类型 xff0c 调用同名方法 这里有2个