• SpringBoot运行流程源码分析------阶段三(Spring Boot外化配置源码解析)


    Spring Boot外化配置源码解析

    外化配置简介

    Spring Boot设计了非常特殊的加载指定属性文件(PropertySouce)的顺序,允许属性值合理的覆盖,属性值会以下面的优先级进行配置。
    
    • 1
    • home目录下的Devtool全局设置属性(~/.spring-boot-devtools.properties,条件是当devtools激活时)
    • @TestPropertySource注解的测试用例。
    • @SpringBootTest#properties注解的测试用例。
    • 命令行参数。
    • 来自SPRING_APPLICATION_JSON的属性(内嵌在环境变量或系统属性中的内联JSON)
    • ServletConfig初始化参数
    • ServletContext初始化参数
    • java:comp/env的JNDI属性
    • Java系统属性(System.getProperties())
    • 操作系统环境变量
    • RandomValuePropertySource,只包含random.*中的属性
    • jar包外的Profile_specific应用属性(application-{profile}.propertis和YAML变量)
    • jar包内的Profile_specific应用属性(application-{profile}.propertis和YAML变量)
    • jar包外的应用配置(application.properties和YAML变量)
    • jar包内的应用配置(application.properties和YAML变量)
    • @Configuration类上的@PropertySource注解
    • 默认属性(通过SpringApplication.setDefaultProperties指定)

    在以上配置方式中,我们经常使用的包括:命令参数,属性文件,YAML文件等内容,以下将围绕他们的运行及相关代码进行讲解。

    ApplicationArguments参数处理

    ApplicationArguments提供了针对参数的解析和查询功能。在Spring Boot运行阶段的章节中我们提到过,通过SpringApplication.run(args)传递的参数会被封装在ApplicationArguments接口中。本节我们来详细了解下ApplicationArguments接口。
    
    • 1
    接口定义及初始化

    首先看一下ApplicationArguments接口的具体方法定义及功能介绍。

    package org.springframework.boot;
    
    import java.util.List;
    import java.util.Set;
     
    public interface ApplicationArguments {
       
    	//返回原始未处理的参数(通过application传入的)
    	String[] getSourceArgs();
    	//返回所有参数的集合,如参数为:--foo=bar --debug,则返回【"foo","debug"】
    	Set<String> getOptionNames();
    	//选项参数中是否包含指定名称的参数
    	boolean containsOption(String name);
    	//根据选项参数的名称获取选项参数的值列表
    	List<String> getOptionValues(String name);
    	//返回非选项参数列表
    	List<String> getNonOptionArgs();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    通过接口定义可以看出,ApplicationArguments主要提供了针对参数名称和值的查询,以及判断是否存在指定参数的功能。

    在Spring Boot的初始化运行过程中,ApplicationArguments接口的实例化操作默认是通过实现类DefaultApplicationArguments来完成的。DefaultApplicationArguments的底层又是基于Spring框架中的命令行配置源SimpleCommandLinePropertySource实现的,SpringCommandLinePropertySource是PropertySource抽象类的派生类。

    以下代码中内部类Source便是SimppleCommandLinePropertySource的子类。

    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);
    		this.args = args;
    	}
    	......
    
    	private static class Source extends SimpleCommandLinePropertySource {
       
    
    		......
    
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    我们再来看SimpleCommandLinePropertySource的构造方法,通过代码会发现默认使用spring的SimpleCommandLineArgsParser对args参加进行解析。

    public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
       
        public SimpleCommandLinePropertySource(String... args) {
       
            super((new SimpleCommandLineArgsParser()).parse(args));
        }
    	//重载的构造方法
        public SimpleCommandLinePropertySource(String name, String[] args) {
       
            super(name, (new SimpleCommandLineArgsParser()).parse(args));
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    除了构造方法之外,SimpleCommandLinePropertySource还提供了不同类型参数信息的获取和检查是否存在的功能,代码如下:

    public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
       
        ......
        //获取选项参数数组    
        public String[] getPropertyNames() {
       
            return StringUtils.toStringArray(((CommandLineArgs)this.source).getOptionNames());
        }
    	//获取是否包含指定name的参数
        protected boolean containsOption(String name) {
       
            return ((CommandLineArgs)this.source).containsOption(name);
        }
    	//获取指定name的选项参数列表
        @Nullable
        protected List<String> getOptionValues(String name) {
       
            return ((CommandLineArgs)this.source).getOptionValues(name);
        }
    	//获取非选项参数列表
        protected List<String> getNonOptionArgs() {
       
            return ((CommandLineArgs)this.source).getNonOptionArgs();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    ApplicatinArguments,或者更进一步说是SimpleCommandLinePropertySource对参数类型是有所区分的,即选项参数和非选项参数。

    选项参数必须以“–”为前缀,参数值可为空,该参数可以通过Spring Boot属性处理后使用,比如在执行jar -jar命令时,添加选项参数“–app.name=spring boot start",在代码中可以通过注解@Value属性及其他方式获取到该参数的值。该参数可以通过逗号分隔多个参数值,或者多次使用同一个参数来包含多个参数的值。

    非选项参数并不要求以“–”前缀开始,可自行定义。非选项参数可以直接在jar -jar命令中定义参数为“non-option"的参数值。

    以上所说的选项参数和非选项参数的解析是在SimpleCommandLinePropertySource构造方法中调用SimpleCommandLineArgsParser中完成的,代码如下:

    class SimpleCommandLineArgsParser {
       
        SimpleCommandLineArgsParser() {
       
        }
    	//解析args参数,返回一个完整的CommandLineArgs对象
        public CommandLineArgs parse(String... args) {
       
            CommandLineArgs commandLineArgs = new 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, arg.length());
                    String optionValue = null;
                    String optionName;
                    //判断是--foo=bar参数格式,还是-foo参数格式,并分别处理获取值
                    if (optionText.contains("=")) {
       
                        optionName = optionText.substring(0, optionText.indexOf(61));
                        optionValue = optionText.substring(optionText.indexOf(61) + 1
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    【技术分享】RK356X Ubuntu 推流USB摄像头
    从零开始,开发一个 Web Office 套件(5):Mouse hover over text
    pandas Series 使用整数索引的一些问题
    零基础自学javase黑马课程第三天
    Azure DevOps (十三) 通过Azure Devops部署一个Go的Web应用
    点赞、收藏必读文章--数据分析的多变量分析
    正余弦算法优化VMD进行信号分析python
    专用神经网络处理器的芯片,cpu可以跑神经网络吗
    中南林业科技大学数据库实验七:存储过程和触发器
    HTTP协议总结
  • 原文地址:https://blog.csdn.net/qq_41934990/article/details/134024359