Spring Boot 参数配置机制

Spring Boot默认会读取Classpath根路径下的application.properties文件和application.yml文件作为配置文件(使用properties文件还是YAML文件就看个人的喜好了,就笔者而言,YAML文件结构更好,看起来更加的直观),并把它们封装到Environment对象。在应用中通过注入Enviroment对象就可以获取到application.properties中定义的所有的属性,也可以直接定义属性占位符进行属性值注入。假设在application.yml文件中进行了如下定义,其中test.prop.c的值中又使用了占位符test.prop.a,这在原Spring中也是支持的语法。

test.prop:
  a: valueA
  b: valueB
  c: ${test.prop.a}C

在Spring Boot应用中有如下这样一个bean定义,它有propA、propB和propC一个三个属性,需要分别从Environment中通过test.prop.atest.prop.btest.prop.c进行注入。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@Data
public class PropResolveSample {
    @Value("${test.prop.a}")
    private String propA;
  
    @Value("${test.prop.b}")
    private String propB;
  
    @Value("${test.prop.c}")
    private String propC;
  
}

这些参数都是可以正常注入的,而且值的来源就来自于定义在application.yml文件中的参数定义,以下是基于Spring Boot和Junit的单元测试代码,可以正常通过。

@SpringBootTest(classes=Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class PropResovleTest {
    @Autowired
    private PropResolveSample propResolveSample;
    @Autowired
    private Environment enviroment;
  
    @Test
    public void test() {
        Assert.assertEquals("valueA", this.propResolveSample.getPropA());
        Assert.assertEquals("valueB", this.propResolveSample.getPropB());
        Assert.assertEquals("valueAC", this.propResolveSample.getPropC());
    
        Assert.assertEquals("valueA", this.enviroment.getProperty("test.prop.a"));
        Assert.assertEquals("valueB", this.enviroment.getProperty("test.prop.b"));
        Assert.assertEquals("valueAC", this.enviroment.getProperty("test.prop.c"));
    }
  
}

其实Spring Boot默认读取的配置文件的位置不仅仅是Classpath根路径,还包括Classpath下的/config路径,当前文件路径和当前文件路径下的config目录。即Spring Boot默认会读取classpath:/application.propertiesclasspath:/config/application.propertiesfile:./application.propertiesfile:./config/application.properties。当这四个路径下都同时存在application.properties文件时,每个application.properties文件都会读取,属性优先级将按照如下顺序,即后面的会覆盖前面的,如果每个路径下的application.properties文件中都定义了一个app.name属性,最终将以file:./config/application.properties中定义的为准。

  • classpath:/
  • classpath:/config/
  • file:./
  • file:./config/

可以通过系统属性spring.config.location来改变读取默认的配置文件的路径,如果有多个路径,多个路径可以使用英文逗号分隔。指定时可以指定的是具体的配置文件路径,也可以是配置文件所在的目录,如果配置的是目录应该以/结尾。同时指定了多个配置文件时后面的配置文件的属性优先级更高。

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties`

配置文件的名称默认是application,如有需要也可以通过系统属性spring.config.name来更改。通过系统属性spring.config.location指定了配置文件的读取路径后将不再读取默认路径下的配置文件了,如果需要在读取默认位置下的配置文件的同时读取自定义的配置文件,可以使用spring.config.additional-location系统属性进行追加。使用spring.config.additional-location系统属性追加的配置文件的属性值的优先级将高于默认位置下的配置文件中的属性值,且同时追加多个配置文件时后面的配置文件的优先级更高。

随机数占位符

在配置占位符时,可以使用random.前缀,之后可以接int、long、uuid等。其中int和long后还可以接参数指定范围,比如random.int(10)表示取10以内的随机数,random.int(10,20)表示取10-20的随机数,取出来的随机数会大于等于10,但是会小于20。随机数占位符会由org.springframework.boot.env.RandomValuePropertySource进行处理。该随机数占位符可以在application.yml文件中使用,也可以直接在bean定义的时候使用。下面的第一段代码就是直接在YAML文件中定义的,而第二段代码的最后两个属性值的注入则是直接通过random占位符进行注入的。

test.random:
  int: ${random.int}
  int10: ${random.int(10)}
  int10-100: ${random.int(10,100)}
  long: ${random.long}
  long-100: ${random.long(100)}
  uuid: ${random.uuid}
  bytes: ${random.bytes}
@Component
@Data
public class RandomExample {
    @Value("${test.random.int}")
    private int randomInt;
  
    @Value("${test.random.int10}")
    private int randomInt10;
  
    @Value("${test.random.int10-100}")
    private int randomInt10_100;
  
    @Value("${test.random.long}")
    private long randomLong;
  
    @Value("${test.random.long100}")
    private long randomLong100;
  
    @Value("${test.random.uuid}")
    private String randomUUID;
  
    @Value("${test.random.bytes}")
    private String bytes;
  
    @Value("${random.int(1000)}")
    private int randomInt1000;
  
    @Value("${random.bytessss}")
    private String bytes2;
  
}

需要注意的是当random.后接的内容不是int、long或uuid时将随机产生一个16位的字节数组,并把它们转换为16进制。输出上面RandomExample对象的toString()会得到类似于下面这样的一个输出:

RandomExample(randomInt=-82870261, randomInt10=9, randomInt10_100=72, randomLong=5569034461519794446, randomLong100=35, randomUUID=be6141ed-afba-43a4-b484-c738f2bc9ce0, bytes=67e403a4d9bf57ea4c0fe53020bff74e, randomInt1000=629, bytes2=e8c741396012ee33060c0bfe1f5e2f52)

访问命令行参数

默认情况下命令行指定的可选参数,即以--开头指定的参数也会被添加到Environment中。比如指定命令行参数为--foo=bar,则可以从Environment中获取到属性foo的值为bar。如果不希望命令行指定的可选参数自动添加到Environment中,则可以通过SpringApplication的setAddCommandLineProperties(false)禁用它。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setAddCommandLineProperties(false);
        app.run(args);
    }
}

profile特性的配置文件

除了加载默认的application.properties文件和application.yml文件外,Spring Boot还会尝试加载application-{profile}.properties文件和application-{profile}.yml文件,其中的profile是当前激活的profile,如果当前没有指定激活的profile,则默认激活的profile是default,则默认情况下还会加载application-default.properties或application-default.yml文件。当激活的profile是abc和def时,则还会尝试加载application-abc.properties文件、application-abc.yml文件、application-def.properties文件和application-def.yml文件。当通用的application.properties中定义的属性在application-{profile}.properties也有定义时,application-{profile}.properties中的将拥有更高的优先级,即profile特性的属性定义会覆盖默认的。在指定需要激活的profile时可以在application.properties文件中进行定义。比如下面代码指定了激活的profile是dev。也可以通过命令行参数--spring.profile.active=dev指定。

spring.profiles.active=dev

yml文件中分profile配置

多profile除了可以为不同的profile定义特殊的配置文件外,还可以把profile下特殊的配置定义在同一个application.yml文件中。yml文件中可以使用---来把配置信息分成多个不同的块,每个块可以使用spring.profiles来指定所属的profile。比如在下面的application.yml配置文件中定义了默认的app.ip为10.10.10.1,当激活的profile包含dev时app.ip的值为10.10.10.2,当激活的profile包含test时app.ip的值为10.10.10.3。

app:
  ip: 10.10.10.1
---
spring:
  profiles: dev
  
app:
  ip: 10.10.10.2
  
---
spring:
  profiles: test
  
app:
  ip: 10.10.10.3

参考文档

https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-external-config.html