SpringBoot入口深入

news2024/11/18 15:33:27

这里写目录标题

  • 1.run()
    • 1.1 程序运行监听器 SpringApplicationRunListeners
    • 1.2 应用参数 ApplicationArguments
    • 启动加载顺序

1.run()

run()方法是一个SpringBoot程序的入口

SpringApplication.run(Application.class, args);

看看方法逻辑

	/**
	 * 运行 Spring 应用程序, 创建并刷新一个新的 应用上下文(应用参数环境)(ApplicationContext)
	 * @param args 应用参数 (usually passed from a Java main method)
	 * @return 一个运行中的应用上下文
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			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;
	}

分步解析一个SpringBoot应用程序是怎么创建出来的

1.1 程序运行监听器 SpringApplicationRunListeners

关注语句

SpringApplicationRunListeners listeners = getRunListeners(args);

这里调用了 getRunListeners()方法,并且把args参数传进去了,这个是SpringApplication的方法,进入看看

	/** 获取运行监听器 */
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		// 创建了一个 Class 数组 [SpringApplication.class, String[].class]
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 创建了一个 SpringApplicationRunListeners 对象返回
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

getRunListeners()方法中创建了一个SpringApplicationRunListeners对象返回,创建途中调用了另外一个成员方法getSpringFactoriesInstances(),往下探

	/** 获取 Spring 工厂实例 */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 用Spring 工厂加载器加载属于 SpringApplicationRunListener.class 这个类型的工厂的名称集合
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

属于 SpringApplicationRunListener.class 这个类型的工厂的名称集合 names:
0 = “org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer”
1 = “org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener”
2 = “org.springframework.boot.devtools.restart.RestartScopeInitializer”
3 = “org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer”
4 = “org.springframework.boot.context.ContextIdApplicationContextInitializer”
5 = “org.springframework.boot.context.config.DelegatingApplicationContextInitializer”
6 = “org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer”
7 = “org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer”
就是说属于 SpringApplicationRunListener的从属类有8个,都是一些初始化器监听器

1.2 应用参数 ApplicationArguments

关注语句

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

ApplicationArguments(应用参数)这个对象,看名字它应该是用来封装SpringBoot应用的参数的,
在分析这个对象之前,先来看参数args是什么参数。

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

在主函数中args是一个string数组参数,而我们的run()方法对应的参数是可变string,没毛病

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

但是好像看完还是不知道这些个String类型的参数是什么参数,有什么作用
那就直接看ApplicationArguments应用参数这个对象

/** 提供用于运行Sping应用程序(SpringApplication)的参数的访问。*/
public interface ApplicationArguments {

	/** 返回传递给应用程序的原始未处理的参数。*/
	String[] getSourceArgs();

	/** 返回所有选项参数的名称。 如参数是 "--foo=bar --debug",返回 ["foo", "debug"]。 */
	Set<String> getOptionNames();

	/** 判断某个选项参数是否存在在选项参数集合里 */
	boolean containsOption(String name);

	/**
	 * 获取指定选项参数的值集合
	 * 如"--foo", 返回 []
	 * 如"--foo=bar", 返回 ["bar"]
	 * 如"--foo=bar --foo=baz",返回 ["bar", "baz"]
	 * 如果指定的选项参数不存在,返回 null
	 */
	List<String> getOptionValues(String name);

	/** 获取非选项参数集合 */
	List<String> getNonOptionArgs();
}

看到这个就很清楚了,特别是选项参数。像jar包的运行命令

  • java -jar xxx.jar
  • java -jar xxx.jar --server.port=8888

这些启动命令行就带有选项参数,所以说args其实就是运行程序的命令行参数。
接下来深入了解一下ApplicationArguments的默认实现DefaultApplicationArguments

package org.springframework.boot;

public class DefaultApplicationArguments implements ApplicationArguments {
	private final Source source; // 源
	private final String[] args; // 原始参数

	public DefaultApplicationArguments(String... args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args); // 用 args 创建一个源
		this.args = args;
	}
	/** 这个 source 是 DefaultApplicationArguments 的一个静态内部类
	  *	继承了 SimpleCommandLinePropertySource (简单命令行属性源)
	  */
	private static class Source extends SimpleCommandLinePropertySource {
	
		Source(String[] args) { super(args); }
		...
	}
}

这里看到DefaultApplicationArguments 封装了一个Source (源)对象,而Source 对象用的是它父类SimpleCommandLinePropertySource (简单命令行属性源)的构造器,深入!

package org.springframework.core.env;

public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
    public SimpleCommandLinePropertySource(String... args) {
        super((new SimpleCommandLineArgsParser()).parse(args));
    }
	...
}

SimpleCommandLinePropertySource (简单命令行属性源)用的是它的父类CommandLinePropertySource<T>的构造器初始化的,并且创建了一个SimpleCommandLineArgsParser对象来解析参数,先看看SimpleCommandLineArgsParser和它的parse()方法

package org.springframework.core.env;

class SimpleCommandLineArgsParser {
    SimpleCommandLineArgsParser() {}

    public CommandLineArgs parse(String... args) {
        CommandLineArgs commandLineArgs = new CommandLineArgs(); // CommandLineArgs 对象,用来保存参数名和参数值
        String[] var3 = args;
        int var4 = args.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String arg = var3[var5];
            if (arg.startsWith("--")) { // 解析"--"开头的参数
                String optionText = arg.substring(2); // 取“--”后面的字符串
                String optionValue = null;
                int indexOfEqualsSign = optionText.indexOf(61); // 获取“=”的位置(61对应ASCLL字符为“=”)
                String optionName;
                if (indexOfEqualsSign > -1) { // 有“=”字符
                    optionName = optionText.substring(0, indexOfEqualsSign);   // 取key
                    optionValue = optionText.substring(indexOfEqualsSign + 1); // 取value
                } else { // 无“=”字符
                    optionName = optionText; // 取key
                }
                
                if (optionName.isEmpty()) { // 如果没有选项参数名,报错
                    throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                }

                commandLineArgs.addOptionArg(optionName, optionValue); // 封装到 commandLineArgs 中
            } else {
                commandLineArgs.addNonOptionArg(arg); // 封装空参
            }
        }
        return commandLineArgs; // 返回
    }
}

也就是说 SimpleCommandLineArgsParser 对象对args按一定规则做参数解析,把原本像--optionName=value这样的参数解析成optionNamevalue,再封装到一个类似键值对包装类的 CommandLineArgs 对象返回。
接下来看 SimpleCommandLinePropertySource (简单命令行属性源)的父类 CommandLinePropertySource< T > 用这个 CommandLineArgs 对象初始化时做了啥

package org.springframework.core.env;

public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
    ...
    public CommandLinePropertySource(T source) { // CommandLineArgs commandLineArgs → T source
        super("commandLineArgs", source);
    }
	...
}

CommandLinePropertySource< T > 又调用了它的父类 EnumerablePropertySource (有限属性源)做初始化,继续进去。

package org.springframework.core.env;

public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
    public EnumerablePropertySource(String name, T source) { // (“commandLineArgs",commandLineArgs) 
        super(name, source);
    }
	...
}

EnumerablePropertySource (有限属性源)又调用了它的父类 PropertySource< T > (属性源)做初始化,继续

package org.springframework.core.env;

public abstract class PropertySource<T> {
    protected final Log logger;
    protected final String name;
    protected final T source;

    public PropertySource(String name, T source) {
        Assert.hasText(name, "Property source name must contain at least one character");
        Assert.notNull(source, "Property source must not be null");
        this.name = name;     // 属性名字默认为 “commandLineArgs"
        this.source = source; // 保存 commandLineArgs 副本
    }
	...
}

可以看到 PropertySource< T > 就是尽头了,一路初始化就是为了封装选项参数的keyvalue,最后得到所有参数的

结构图:
在这里插入图片描述

启动加载顺序

入口

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

SpringApplication 的初始化过程

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader; // 这里传进来的资源加载器是 null
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath(); // Web程序类型是 SERVLET
		// 
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在这里new SpringApplication(primarySources)的时候调用了loadFactoryNames()
加载的工厂名:org.springframework.context.ApplicationContextInitializer
在这里插入图片描述

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

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

相关文章

OVIS数据集代码解析

OVIS数据集代码解析 OVIS数据集格式整体和COCO类似&#xff0c;但是是以video的形式存储的&#xff0c;对应的解析代码见&#xff1a;https://github.com/qjy981010/cocoapi/blob/main/PythonAPI/pycocotools/ovis.py。 由于OVIS仅train提供了标注&#xff0c;因此&#xff0…

前端埋点实现方案

前言 领导今天又来活了&#x1f623;&#xff0c;要记录每个页面的停留时间&#xff0c;以及页面的操作&#xff0c;是由哪个页面跳转过来的&#xff0c;给每个页面生成GUID上报给服务端&#xff0c;并且需要携带设备型号和设备唯一标识&#x1f644; 名称解释 UV&#xff0…

蓝牙运动耳机排行榜,目前排名最好的运动耳机推荐

对于运动人士来说&#xff0c;运动过程中佩戴着耳机听歌&#xff0c;既能让运动变得更加激情有动力&#xff0c;同时还能释放压力&#xff0c;放松心情。在选择运动耳机方面的要求也高&#xff0c;运动耳机不仅需要佩戴稳固舒适&#xff0c;它的防水性能也不能差&#xff0c;当…

Java并发常见面试题(一)

进程和线程 进程 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。 在Java中&#xff0c;当我们启动main函数其实就是启动了一个JVM进程&#xff0c;而main函数所在的线…

封装自己的 jquery 框架

说到jquery&#xff0c;那就给大家先简单的介绍一下什么是jquery jquery是JavaScript函数的一种封装。jQuery封装了JavaScript常用的功能代码&#xff08;函数&#xff09;&#xff0c;提供一种简便的JavaScript设计模式&#xff0c;优化HTML文档操作、事件处理、动画设计和Aja…

机器人运动学【2】

目录1.刚体状态的表达2.顺向运动学及DH表3.逆向运动学1.刚体状态的表达 我们前面已经学习了刚体移动和转动的表达&#xff0c;那么怎么将两者在数学上结合呢&#xff1f;这里我们开始构造如下矩阵&#xff0c;记作: 下面我们来看一下只有移动情况下的刚体的描述&#xff1a; …

使用canal解决Mysql和Redis数据同步(TCP)

使用canal解决Mysql和Redis数据同步(TCP) 工作原理分析 我们在面试的时候常常听面试官问这么一个问题&#xff1a;你们的Mysql和Redis怎么做数据同步的&#xff0c;根据不同的业务场景又很多方案&#xff0c;你可能会说先写库再删缓存&#xff0c;或者延迟双删或其他方案。今…

springboot集成flowable简单实例入门

此案例是demo。功能有创建流程、完成审批、生成流程图。适合有java基础的人员看。 第一步.画流程图 resources资源包下&#xff0c;新建processes包&#xff0c;新建一个文件&#xff0c;我命名他apply-rest.bpmmn20.xml。bpmn20.xml后缀文件是流程图配置文件。idea的右下角的…

ImageMol

ai圈也是卷的狠啊~~ 又发现一个全球首个分子图像自监督学习框架来也 分子是维持物质化学稳定性的最小单位。对分子的研究&#xff0c;是药学、材料学、生物学、化学等众多科学领域的基础性问题。 分子的表征学习&#xff08;Molecular Representation Learning&#xff09;是…

实践 | 大型基金管理公司数据脱敏体系建设

金融行业数据脱敏安全管理要求高、数据类型复杂、脱敏数据规模庞大。作为业内领先&#xff0c;且支持信创环境的数据安全产品&#xff0c;美创数据脱敏系统在金融行业应用广泛&#xff0c;可满足各类复杂环境下的数据脱敏需求&#xff1a; 台州银行—分布式大数据平台下的及时脱…

SQL Server数据库理论篇(进行时......)

SQL Server数据库理论篇 一. 数据库的关系分类 1.1.0 关系型数据库和非关系数据库区别&#xff1f; 结论&#xff1a;两种数据库的本质在于存储数据的形式不同 1.关系型数据库概念 关系型数据库最大的特征就是表&#xff0c;我们将对象的属性映射为表的一个个列名&#xff…

(九)centos7案例实战——redis一主二从三哨兵高可用服务搭建

前言 本节内容是使用centos服务器搭建一套高可用的redis服务&#xff0c;采用的是一主二从三哨兵的模式。 需要注意的是搭建集群的过程中&#xff0c;我们要保证集群服务器之间可以相互访问&#xff0c;并且redis所需要访问的端口是开放的。我们从redis的下载&#xff0c;源码…

ffmpeg 安装教程

官网&#xff1a;Download FFmpeg window 转&#xff1a;ffmpeg安装教程_moon son的博客-CSDN博客_ffmpeg安装 然后解压&#xff0c;配置全局变量环境。点击“系统属性->高级系统设置->环境变量->系统变量”&#xff0c;选择“Path”条目&#xff0c;点击“编辑->…

将项目部署到Linux系统上

目的是让我们的项目在linux上也能运行起来 有两种部署方式&#xff0c;手工部署或者是通过shell脚本自动部署 手工部署 准备工作&#xff1a;使用ifconfig指令查出服务器的ip地址&#xff1a;192.168.58.130 1.在本地Idea中开发一个springboot项目&#xff0c;并且打包成ja…

以太网模块的传输距离怎么看

光模块的关键标准组织主要有两个&#xff1a;IEEE和MSA。其中GBASE开头的标准主要是IEEE802.3提出与定义的。要弄清光模块的传输距离&#xff0c;首先让我们要弄清楚它的命名是怎样的。 100G光模块的命名规则&#xff1a; 400G光模块的命名规则&#xff1a; 其中100G和400G光模…

SpringBoot整合Mybatis方式1:使用XML方式整合Mybatis

SpringBoot整合Mybatis简介SpringBoot整合Mybatis方式1&#xff1a;使用XML方式整合Mybatis1.用idea创建一个添加mybatis需要的相关依赖的工程。2.准备数据库和表3.创建表映射类4.创建mapper文件4.1 创建UsersMapper.xml文件&#xff0c;并添加sql语句4.2 创建mapper对应的代理…

Docker的Cgroup资源限制

Docker通过Cgroup来控制容器使用的资源配额&#xff0c;包括 CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配颡和使用量控制。 Cgoup 是CotrolGroups 的缩写&#xff0c;是Linux 内核提供的一种可以限制、记录、隔高进程组所使用的物理资源&#xff08;如CPU、内存…

基于jsp的学生培训管理系统

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【教学类-18-01】20221123《蒙德里安红黄蓝格子画》(大班)

效果展示&#xff1a; 单页效果 多页效果 预设效果 背景需求&#xff1a; 2022年11月23日&#xff0c;作为艺术特色幼儿园&#xff0c;蒙德里安风格装饰在我们幼儿园的环境中。 蒙德里安是几何抽象画派的先驱&#xff0c;以几何图形为绘画的基本元素&#xff0c;与德士堡等创…

MyBatis-Plus 和swagger

MyBatis-Plus 1.1MyBatis Plus 简介 mybatisplus 官网&#xff1a; https://baomidou.com/ MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c; 为简化开发、提高效率而生。 1.2主要特性&a…