SpringBoot源码分析(4)--Environment(下)/配置文件加载原理

SpringBoot源码分析

  1. SpringBoot源码分析(1)–@SpringBootApplication注解使用和原理/SpringBoot的自动配置原理详解
  2. SpringBoot源码分析(2)–SpringBoot启动源码(万字图文源码debug讲解springboot启动原理)
  3. SpringBoot源码分析(3)–Environment简介/prepareEnvironment准备环境(万字图文源码debug分析)

文章目录

  • 一、前言
  • 二、Spring Boot 配置优先级
    • 2.1、配置文件加载优先级
      • 总结
    • 2.2、如何加载外部配置文件
  • 三、springboot常见的配置参数
    • 3.1、命令行参数
    • 3.2、System Properties与System Environment的区别
    • 3.3、系统参数(System Properties)
    • 3.4、系统环境参数(System Environment)
  • 四、Environment读取配置属性
    • 4.1、传统方式获取environment配置
    • 4.2、SpringBoot2.X 引入更强大的Binder获取environment配置方式

一、前言

上一篇《SpringBoot源码分析(3)–Environment简介/prepareEnvironment准备环境(万字图文源码debug分析)》写完后还有些问题没分析完,此篇文章接着上一篇继续讲。

本文基于spring-boot-2.2.14.BUILD-SNAPSHOT源码分析Environment及配置文件加载原理。

二、Spring Boot 配置优先级

上一篇文章源码分析中有体现过配置的优先级,此处做一下总结。

以下是常用的 Spring Boot 配置形式及配置属性加载的顺序(优先级由高到低):

  1. 命令行参数,如 java -jar springboot.jar --name="Java项目"
  2. 命令行中的 SPRING_APPLICATION_JSON 指定参数, 如 java -Dspring.application.json='{"name":"Java技术栈"}' -jar springboot.jar
  3. servletConfigInitParams 初始化参数;
  4. servletContextInitParams 初始化参数;
  5. JNDI参数(如 java:comp/env/spring.application.json);
  6. systemPropertiesJava系统参数(来源:System.getProperties()或者(-d命令行参数));
  7. systemEnvironment操作系统环境变量参数;
  8. RandomValuePropertySource 随机数,仅匹配:ramdom.*
  9. profile配置文件(`application-{profile}.properties(YAML)
  10. 配置文件(`application.properties(YAML)
  11. @Configuration 注解类上的 @PropertySource 指定的配置文件
  12. 通过SpringApplication.setDefaultProperties 指定的默认属性

以上所有形式的配置都会被加载,当存在相同配置内容时,高优先级的配置会覆盖低优先级的配置;存在不同的配置内容时,高优先级和低优先级的配置内容取并集,共同生效,形成互补配置。

我们这里说的高优先级的配置会覆盖低优先级的配置,其实原理是加载属性配置时按优先级读取配置,当读取到配置后即返回。所以给人的感觉是高优先级覆盖了低优先级。

2.1、配置文件加载优先级

我们在项目中使用最多的配置文件是application.properties、application.yml、application-test.yml等文件。此处我们讲解一下springboot加载这些配置文件的优先级。

springboot启动时默认扫描下面四个目录,优先级如下(优先级从高到低):

  1. file:./config/ ( 项目根路径下的config文件夹)
  2. file:./ (项目根路径)
  3. classpath:/config/ (类路径/resources下的config文件夹)
  4. classpath:/ (类路径/resources)

文件加载顺序如下:

  1. 先按优先级加载四个目录下的application文件,同一目录下propertites比yml优先。properties > xml > yml > yaml
  2. 然后按优先级加载四个目录下的application-{profile}文件,同一目录下propertites比yml优先。properties > xml > yml > yaml

注意:此处的加载顺序只是springboot加载文件时的顺序,并不是读取配置文件中属性的顺序。
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第1张图片

配置属性加载顺序如下:

  1. 先按优先级加载四个目录下的application-{profile}文件,同一目录下propertites比yml优先。properties > xml > yml > yaml
  2. 然后按优先级加载四个目录下的application文件,同一目录下propertites比yml优先。properties > xml > yml > yaml

注意:此处的加载顺序才是Environment获取配置的顺序,即environment.getProperty(key)时的顺序
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第2张图片
问题:为什么会有文件加载顺序与配置属性加载顺序两种顺序呢?

springboot默认是按照文件加载顺序,即遍历profile然后加载classpath:/,classpath:/config/,file:./,file:./config/四个目录下的properties、xml、yml、yaml文件,最后按profile为维度进行反转,所以文件加载的顺序与配置属性加载的顺序是相反的,具体原理可以看上一篇文章。

测试demo
如下图,当同时有application.propertites与application-{profile}.propertites文件时,我们可以看到属性加载时是以application-{profile}.propertites为优先的。
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第3张图片

总结

1.加载目录优先级从高到低
file:./config/ :项目根目录找config文件夹下找配置文件。
file:./ :根目录下找配置文件
classpath:/config/ :resources下找cofnig文件夹下找配置文件。
classpath:/ :resources下找配置文件。

2.在同一个目录,application.yml和application.properties同时存在,先读取application.properties
文件优先级:properties > xml > yml > yaml

3.先读取到的配置文件,不会因为在后面其他目录再次读到同名的配置文件而被替换。以第一次读到的为准

4.有子module的工程,子module目录下的配置文件和子module目录下一层config目录的配置文件不会加载。只会加载resources下面的配置文件

2.2、如何加载外部配置文件

在实际业务使用中,我们可能遇到项目被打成jar包,然后将配置文件放在jar包外面,怎么能指定加载外部的配置文件呢?

springboot提供了一个配置属性spring.config.location,当配置了该值,则从该值中加载配置文件,否则从classpath:/,classpath:/config/,file:./,file:./config/这四个目录下加载文件。所以我们可以在启动命令中指定外部路径。

java  -jar ../java/*.jar --spring.config.location=../conf/application.yml,classpath:/configs/default.properties

注意事项:支持通过命令行参数的方式指定多个配置文件,使用英文半角 , 隔开即可。

如果你通过spring.config.location指定的不是一个文件而是一个目录,在路径最后务必添加一个"/"结束,然后结合spring.config.name进行组合配置文件,组合示例如下:

# 加载/configs/application.properties 或 /configs/application.yml(默认文件名)
java -jar project.jar --spring.config.location=classpath:/configs/

# 加载/configs/custome.properties 或 /configs/custome.yml
java -jar project.jar --spring.config.location=classpath:/configs/ --spring.config.name=custome

注意事项:spring.config.name该配置参数默认值为application,所以如果只是指定了spring.config.location并为目录形式,上面示例中会自动将spring.config.name追加到目录路径后,如果指定的spring.config.location并非是一个目录,这里会忽略spring.config.name的值。

上面内容中提出了一些新的概念,如命令行参数、启动参数、系统参数、系统环境参数等等。下面就稍微了解一下。

三、springboot常见的配置参数

3.1、命令行参数

--key=value的形式添加命令行参数

方式一: idea启动时添加
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第4张图片
方式二: jar包启动时添加

java  -jar ../java/*.jar --spring.config.location=../conf/application.yml --logging.config=../conf/logback-spring.xml 

方式三: 启动类中添加参数

@SpringBootApplication
public class Demo3Application {
	public static void main(String[] args) {
	        //添加命令行参数
	        List<String> argsList = new ArrayList<>();
	        argsList.add("--spring.profiles.active=sit");
	        if (args != null) {
	            argsList.addAll(Arrays.asList(args));
	        }
	        SpringApplication.run(Demo3Application.class, StringUtils.toStringArray(argsList));
	}
}

SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第5张图片

3.2、System Properties与System Environment的区别

System Properties与System Environment的变量很相近,此处说明一下两者的区别。

System Environment 指的是操作系统的环境变量,而 System Properties 指的是java 程序jvm的系统变量

常见的JVM属性
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第6张图片

3.3、系统参数(System Properties)

-Dkey=value的形式添加JVM参数

方式一: 在idea启动参数中添加
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第7张图片
SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第8张图片
方式二: jar命令启动时添加

java -jar target/xxx.jar -Dserver.port=9090 -Dserver.context-path=/test

方式三: 代码中添加

在代码里通过System.setProperty(key, value)进行设置

在java中获取jvm的系统变量代码如下:

// 获取所有的环境变量
Properties properties = System.getProperties();
// 获取指定的环境变量 如java.class.path
String property = System.getProperty("java.class.path");

3.4、系统环境参数(System Environment)

在java中获取操作系统的环境变量的代码如下:

// 获取全部的环境变量
Map<String, String> systemEnvironment = System.getenv();
// 获取某个环境变量 比如:PATH
String path = System.getenv("PATH");

SpringBoot源码分析(4)--Environment(下)/配置文件加载原理_第9张图片

四、Environment读取配置属性

4.1、传统方式获取environment配置

1.springboot启动类,传统从environment中获取值的方式environment.getProperty

@SpringBootApplication(scanBasePackages = {"com.example"})
public class Demo3Application {


    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(Demo3Application.class,args);
        
        Environment environment = run.getEnvironment();
        System.out.println(environment.getProperty("server.port"));
        System.out.println(environment.getProperty("people.man.name"));
    }
}

2.application.yml

server:
  port: 8080

people:
  man:
    name: 张三
    age: 12

a.b.my-first-key: hello spring environment

4.2、SpringBoot2.X 引入更强大的Binder获取environment配置方式

1.先看一个简单的示例,如何使用Binder绑定environment环境变量

@SpringBootApplication(scanBasePackages = {"com.example"})
public class Demo3Application {


    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(Demo3Application.class,args);
        
        Environment environment = run.getEnvironment();
         //此处调用Binder.get方法获取一个Binder
        Binder binder = Binder.get(environment);
         Integer port = binder.bind("server.port", Integer.class).get();
        //bind绑定yml文件中的配置key到一个具体的对象中
        People people= binder.bind("people.man", People.class).get();
        System.out.println(port);
        System.out.println(people.toString());
    }
}

输出结果:

8080
People(name=张三, age=12)

通过以上示例,我们可以看到Binder可以很方便的动态绑定对象,类型转换。Binder还可以绑定Map,list等。我们可以发现源码中也有很多地方使用了Binder绑定配置对象,是一个非常好用强大的类。

你可能感兴趣的:(springbooot原理,spring,boot,springboot源码)