Spring Boot允许将配置外部化,以便在不同的环境中使用相同的程序代码。可以使用各种外部配置源,包括Java属性文件、YAML文件、环境变量、命令行参数。配置属性值可以通过@Value注解直接注入到bean中,或者通过@ConfigurationProperties注解绑定到结构化对象。
Spring Boot中的配置具有顺序性,该顺序允许程序员合理的覆盖属性值,顺序高的配置值可以覆盖顺序低的配置值,优先级从低到高如下:
配置文件的优先级顺序如下:
建议在项目中使用相同格式的配置文件,统一为 .properties 或 .yml 格式。假设在相同的目录中同时存在两种类型的配置文件,.properties优先
@Component
public class MyBean {
@Value("${name}")
private String name;
}
以上代码使用 @Value注解读取配置文件的name值。可以应用不同的场景:
jar包内部 application.properties 中配置的name属性
运行新环境时,jar外部 application.properties 文件中name属性值可以覆盖jar包内部的属性值
测试时,可以通过命令行覆盖属性值
java -jar app.jar --name="Spring"
默认情况下,Spring Boot 可以转化命令中 以 --开头的参数,如 --server.port=9000,并把它们添加到Spring Environment环境中,命令行的参数始终偶先配置文件中的参数。
如果不想将命令行中的属性配置添加到 Environment中,可以通过代码禁用该行为:
SpringApplication.setAddCommandLineProperties(false)
环境变量跟系统属性可能会存在一些属性名称上的冲突,为了解决此类问题。Spring Boot允许将属性代码快编码为单个的JSON结构。启动应用程序时,spring.application.json (SPRING_APPLICATION_JSON) 属性值将被解析并添加到 Environment环境中。
例如,SPRING_APPLICATION_JSON
// 方式一
java -jar target/springboot-study-0.0.1-SNAPSHOT.jar --spring.application.json='{\"name\":\"test\"}'
// 方式二
java -jar target/springboot-study-0.0.1-SNAPSHOT.jar -Dspring.application.json='{\"name\":\"test\"}'
系统启动时,Spring Boot 自动从指定的目录加载 application.properties、
application.yaml 配置文件。指定目录(优先级顺序从低到高)如下:
如果不喜欢默认的配置文件名 application,可以通过 spring.config.name 在命令行中指定其他配置文件名称,如下指定 myproject.properties 或 myproject.yaml 文件:
$ java -jar myproject.jar --spring.config.name=myproject
也可以显示指定系统加载的文件,多个目录之间使用逗号进行分隔
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
大多数情况下,每个spring.config选项关联单个文件或者目录。多个目录之间按照定义顺序进行处理,后面的位置可以覆盖前面位置的值。spring.config.location配置的目录将会替换默认的读取路径。例如 spring.config.location 配置为 “optional:classpath:/custom-config/,optional:file:./custom-config/”,Spring Boot 读取的完整路径为:
optional:classpath:custom-config/
optional:file:./custom-config/
如果只是新增路径,然不是替换默认路径。可以使用 spring.config.additional-location 配置。从其他位置加载的属性配置将覆盖默认的属性配置。例如 spring.config.additional-location 配置为 optional:classpath:/custom-config/,optional:file:./custom-config/
, Spring Boot 将会读取的路径为:
Spring Boot允许你有选择性的覆盖另一个配置中的值。开发人员可以在应用程序中为应用程序提供默认值。然后在运行时指定一个特定的路径位置然后覆盖这些默认值。
默认情况下,当指定的配置文件路径不存在时,Spring Boot 将会终止启动应用程序,并抛出异常 ConfigDataLocationNotFoundException。
如果你想指定路径,但并不确定该路径是否一直存在,你可以加上 optional: 前缀。spring.config.location 、spring.config.additional-location都可以使用 这个前缀,也可以使用 spring.config.import 声明。例如, spring.config.import 值为 optional:file:./myconfig.properties 允许应用程序启动,及时 myconfig.properties文件不存在。
如果想忽略ConfigDataLocationNotFoundException异常,并继续启动应用,可以使用 spring.config.on-not-found,值设置为 ingore。系统使用SpringApplication.setDefaultProperties(…) 或 system/environment 中的值
如果配置路径中包含 * 号,则它将被视为通配符位置。加载配置文件时会将子目录也考虑进去。例如 需要配置Redis、Mysql配置文件,考虑将这两部分配置文件分开,方便管理。这将导致两个文件
这种情况需要使用通配符进行处理,让Spring Boot识别并加载这两个目录下的文件,如 config/*/。默认情况下,Spring Boot 在默认的搜索位置搜索 config/*/ 目录,jar包外部的 /config 文件夹也会被识别加载。
通配符位置仅适用于外部目录,不能在classpath: 下 使用通配符
Spring Boot 允许使用 application-{profile} 加载不同环境的配置文件。例如,应用程序在 prod 生产环境中启动,则配置文件命名为 application-prod.yml。跟标准的配置文件加载流程类似,特定环境下profile配置文件会覆盖默认的配置文件。
如果多个 profile 文件被指定,默认应用后面的profile文件 (last-wins strategy),如 同时在 prod、live profile配置文件中指定spring.profiles.active属性,application-live.properties 配置文件中的值 覆盖 application-prod.properties.
last-wins strategy应用于目录组级别,即相同的目录组,后面文件的配置属性覆盖前面文件的配置属性。还是使用上面的案例,同时指定 prod、live 配置文件,如下:
/cfg
application-live.properties
/ext
application-live.properties
application-prod.properties
使用spring.config.location属性,配置值为: classpath:/cfg/,classpath:/ext/,则文件处理顺序如下:
1. /cfg/application-live.properties
2. /ext/application-prod.properties
3. /ext/application-live.properties
将 classpath:/cfg/;classpath:/ext/ 使用分号进行分隔时,则/cfg,/ext目录被当成同一个目录级别,则文件处理顺序如下:
1. /ext/application-prod.properties
2. /cfg/application-live.properties
3. /ext/application-live.properties
系统使用默认的profile配置文件,默认值为 [default],即没有显示指定profile配置文件时,则使用 application-default 配置文件。
应用程序可以使用spring.config.import属性从其他目录导入配置文件,导入的文件被当成原配置文件的一部分进行处理,如下:
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
上述配置将导入当前目录下的dev.properties(如果文件存在)。dev.properties属性文件的值将优先处理,在dev.properties中可以重新定义 spring.application.name的值。
在properties/yaml 单个文件中导入的顺序并不重要,如下两个配置它们的结果是一样的。
spring:
config:
import: "my.properties"
my:
property: "value"
my:
property: "value"
spring:
config:
import: "my.properties"
在上述的两个示例中,mysql.properties中的值将优先于文件中定义的属性值。使用 spring.config.import 导入多个文件时,目录文件按顺序处理,后面导入的文件优先级高。
一些云平台需要导入外部文件系统的配置文件,应对这种场景,需要告诉Spring Boot如何加载他们。
假如需要在application.properties 导入 /etc/config/myconfig文件,使用示例如下
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
etc/
config/
myapp/
username
password
假设存在如上目录配置文件,需要同时读取他们的配置属性。当然可以使用 spring.config.import 一个个的导入,但是可以使用以下更直观的方式导入:
spring:
config:
import: "optional:configtree:/etc/config/"
可以在Environment 环境变量中使用 myapp.username 、myapp.password 配置属性。
如果同一个父目录,存在多个子配置目录,可以使用通配符,减少代码代码量,如下:
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password
spring:
config:
import: "optional:configtree:/etc/config/*/"
application.properties 、applicaiton.yml中的属性值在使用时会经过 Environment处理,因此配置可以关联之前已经定义的值(从系统属性、环境变量中关联)。${name}占位符可以使用在任何位置。属性占位符还可以指定默认值,如 ${name:default}.
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
username 可能其他地方没有设置,配置默认值;app.name为设置为 MyApp,因此description的最终结果为:MyApp is a Spring Boot application written by Unknown
.
Spring Boot 允许将单个文件拆分程多个文件,每个文件单独添加,文档按从上到下的顺序处理,后面的文件可以覆盖前面文件中定义的属性。YAML支持多文档语法,— 字符串表示一个文档的结尾,下一个文档的开始。
以下配置代表两个逻辑文件
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
对于properties属性文件 使用 #— 、!— 字符串分隔文件
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
在一些场景中,当条件满足时,需要激活相关的属性配置。可以使用 spring.config.activate.*. 有条件的技术配置属性。
Property | Note |
---|---|
on-profile |
表达式与配置文件匹配,才能激活相关属性 |
on-cloud-platform |
当配置处于活动状态,CloudPlatform配置值才被有效 |
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
YAML是一个分层的、格式良好的数据配置文件,跟JSON数据一样,也是Key-Value数据格式存储。Spring Boot 内部使用SnakeYAML 第三方 jar包 支持 YAML格式文件的解析。
如果使用Starter包,SnakeYAML jar包已经被包含在 spring-boot-starter.
YAML 文档根据其分层结构转换为 Key-Value键值对存储在Spring Environment 环境中,例如:
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"
Spring Environment 内部存储的数据结构如下:
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
同时,YAML支持组数配置,使用 [index] 解析器表示数组下标,如:
my:
servers:
- "dev.example.com"
- "another.example.com"
上述example将被转换为:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
Spring Framework 提供了两个类来加载YAML配置文件
RandomValuePropertySource 类用于注入随机配置属性(例如,配置秘钥、测试用例)。可以生成Integer、Long、uuids、strings,配置如下:
my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"
使用@Value(“${property}”)注解注入属性文件时,有些时候可能存在问题。尤其是使用多个属性、或者数据分层时,Spring Boot 提供了另一种处理属性的方法,它允许强类型bean管理、验证应用程序的配置。
绑定Bean中定义的属性值(Get、Set方法),使用如下:
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// getters / setters...
public static class Security {
private String username;
private String password;
private List roles = new ArrayList<>(Collections.singleton("USER"));
// getters / setters...
}
}
Bean 与 配置文件之间的对应关系为:
Spring Boot 支持以构造函数方式注入配置属性值,代码如下:
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List<String> getRoles() {
return this.roles;
}
}
}
@ConstructorBinding注解用于指定使用构造函数绑定配置属性值,内部类的构造函数也可以使用该注解绑定属性值。
通过构造函数绑定属性值时,可以使用@DefaultValue注解设置属性默认值。
引用之前的带啊,如果文件中没有配置 Security值,MyProperties 实例中 security字段设置为null。为了避免null值,可以通过 @DefaultValue 指定默认值,如下:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
使用构造函数绑定,Spring Boot应用程序必须开启 @EnableConfigurationProperties 属性、或者配置属性扫描。不能使用构造函数绑定来创建 @Component 声明的bean
Spring Boot 提供 @ConfigurationProperties 声明一个class文件,并将它注册为bean。可以单个启动配置属性,也可以通过扫描启动多个配置属性。
使用@ConfigurationProperties注释的类可能不适合扫描,例如开发自定义的配置,并希望有条件的启用他们。此类情况请使用@EnableConfigurationProperties注解要处理的class类型。需要跟 @configuration配合使用。
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
进行配置文件扫描时,需要在应用程序增加 @ConfigurationPropertiesScan 注解,一般加在启动类上。默认情况下,从注解中声明的package进行扫描,如果需要指定特殊的package,代码如下:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
Spring Boot 使用宽松的规则来匹配配置文件中对的key值,跟class内定义的字段值。
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Property | Note |
---|---|
my.main-project.person.first-name |
properties、yml文件推荐使用方式,最后的字段值自动转换为驼峰式命名 |
my.main-project.person.firstName |
标准的驼峰式命名 |
my.main-project.person.first_name |
.properties、yml 文件可选的数据格式之一 |
MY_MAINPROJECT_PERSON_FIRSTNAME |
大写形式,在定义系统环境变量中推荐使用 |