24. 外部化配置
Spring Boot运行使用外部化配置,方便在不同的花姐中使用相同的代码.你可以使用properties文件,YAML文件,环境变量和命令行参数来外化配置。属性值可以使用@Value注解直接注入到bean中,通过Spring的Environment抽象访问或通过@ConfigurationProperties绑定到结构化对象中.
SpringBoot使用了一个非常特殊的PropertySource先后顺序,它被设计为允许对value重写。Properties按以下先后顺序考虑:
- Devtools设置的全局属性作用的主目录(当devtools是active状态时是~/.spring-boot-devtools.propertis目录)
- @TestPropertySource注解作用的tests目录.
- @SpringBootTest#properties注解属性作用的tests目录
- 命令行参数
- SPRING_APPLICATION_JSON属性的配置(嵌入在环境变量或系统属性中的内嵌json)
- ServletConfig初始化的参数
- ServletContext初始化的参数
- java:comp/env的JNDI属性
- java System属性(System.getProperties())
- OS环境变量
- 一个只有随机属性的RandomValuePropertySource。
- jar包外部的Profile-specific application properties配置(application- {profile} .properties和YAML变体)
- jar包内部的Profile-specific application properties配置(application- {profile} .properties和YAML变体)
- jar包外的application properties(application.properties和yaml variants)
- jar包内的application properties(application.properties和yaml variants)
- @Configuration注解类的@PropertySource注解
- 默认属性(使用SpringApplication.setDefaultProperties指定)。
提供一个具体的例子,假设你开发一个使用name属性的@Component:
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
在你的classpath(例如你的jar包中),你可以有一个application.propertiees文件,为name属性提供一个默认值.当在新环境中运行时,可以在jar外部提供覆盖名称的application.properties;并且对于一次性环境,还可以使用特定的命令行开关启动(例如javar -jar app.jar --name="Spring").
24.1 配置随机值
RandomValuePropertySource用于注入随机值(例如测试用例)。 它可以产生整数,长整数,uuid或字符串,例如:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
random.int *语法是OPEN value(,max)CLOSE,其中OPEN,CLOSE是任何字符和值,max是整数。 如果提供了max,那么value是最小值,max是最大值
24.2 访问命令行属性
默认情况下,SpringApplication会将任何命令行选项参数(以“ - ”开头,例如--server.port = 9000)转换为一个属性并将其添加到Spring环境中。 如上所述,命令行属性始终优先于其他属性源。
如果不想将命令行属性添加到环境中,可以使用SpringApplication.setAddCommandLineProperties(false)禁用它们。
24.3 Application属性文件
SpringApplication将从以下位置的application.properties文件加载属性,并将它们添加到Spring环境(先后顺序,后面的覆盖前面的):
- 当前目录的/config子目录
- 当前目录
- classpath的/config包
- classpath根目录
还可以使用YAML('.yml')文件替换“.properties”。
如果你不喜欢application.properties作为配置文件名,你可以通过指定一个spring.config.name环境属性指定你想要的文件名。 您还可以使用spring.config.location环境属性(以逗号分隔的目录位置或文件路径列表)引用显式位置。
$ java -jar myproject.jar --spring.config.name=myproject
或
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
spring.config.name和spring.config.location用于非常早地确定哪些文件必须加载,因此必须将它们定义为环境属性(通常为OS env,系统属性或命令行参数)。
如果spring.config.location包含目录(而不是文件),则需要在根目录/中结束加载(并且在加载之前附加从spring.config.name生成的名称,包括特定于配置文件的文件名).在spring.config.location中指定的文件按原样使用,不支持指定于配置文件的yaml variants,并且会被任何指定的properties文件的属性覆盖.
通常使用classpath:,classpath:/config,file:,file:config/作为默认搜索路径,而不考虑spring.config.location的值.搜索路径按低到高得顺序排序(file:config/ wins)。如果你指定了自己的路径,那它优先于默认路径,并使用相同的从最低到最高的优先级排序.这样你可以在application.properties(或者使用spring.config.name选择的任何其他基本名称)中设置默认值,并在运行时使用其他文件覆盖它,保留默认值。而不考虑spring.config.location的值
如果使用环境变量而不是系统属性,大多数操作系统不允许使用句号分隔的key name,但您可以改用下划线(例如SPRING_CONFIG_NAME,而不是spring.config.name)。 如果您在容器中运行,则可以使用JNDI属性(在java:comp/env中)或servlet上下文初始化参数,而不是环境变量或系统属性。
24.4 Profile-specific配置
除了application.properties文件外,还可以使用命名约定application-{profile}.properties定义特定于配置文件的属性。 环境具有一组默认配置文件(默认为[default]),如果没有设置活动配置文件(即如果没有显式激活配置文件,则加载来自application-default.properties的属性)。
Profile-specific properties属性跟标准的application.properties相同的位置加载,Profile-specific properties的文件总说覆盖非特定的文件,而不管Profile-specific的文件是否在jar包的内部还是外部
如果您在spring.config.location中指定了任何文件,则不会考虑这些文件的profile-specific文件的变体。 如果您还希望也使用特定于配置文件的属性,请使用
spring.config.location
中的目录。
24.5 属性中的占位符
当使用application.properties中的值时,它们将通过现有环境进行过滤,以便可以引用回先前定义的值(例如,从系统属性)。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
24.6 使用YAML而不是properties
YAML是JSON的超集,因此是一种非常方便的用于指定分层配置数据的格式。 只要你的类路径上有SnakeYAML库,SpringApplication类就会自动支持YAML作为properties的替代。
如果你使用'Starters'将会通过spring-boot-starter自动提供SnakeYAML。
24.6.1 加载YAML
Spring Framework提供了两个方便的类,可以用来加载YAML文档。 YamlPropertiesFactoryBean将YAML作为property加载,YamlMapFactoryBean将YAML作为map加载 例如,以下YAML文档:
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App
转成properties文件是这样:
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
YAML集合表示为具有下标[index]的属性,例如:
my:
servers:
- dev.bar.com
- foo.bar.com
转成properties文件是这样:
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com
要使用Spring的DataBiner(这是@ConfigurationProperties所做的)绑定上述所述属性,你需要在目标bean中具有一个java.util.java属性,并且设置setter方法,或者 使用可变值初始化它,例如 这将绑定到上面的属性
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
24.6.2 在spring环境中将YAML暴露为propertiesjjj
可以用YamlPropertySourceLoader类在Spring环境中将YAML暴露为PropertySource。这允许你使用具有占位符语法的@Value注解来访问YAML属性.
24.6.3 多profile YAML配置
你可以通过使用spring.profiles key指定配置何时使用,在单个文件中指定多个profile配置的YAML文档如下:
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production
server:
address: 192.168.1.120
如上所述,如果development profile处于活动状态,则server.address属性为127.0.0.1,否则该属性为192.168.1.100
如果在spring context启动时没有显示的激活profile,则激活默认profile。所以在这个YAML中,我们为security.user.password设置了一个值,而该值仅在"default"profil中可用.
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
而下面这个示例中,密码始终设置,因为它不附加到任何其他的profile,并且必须根据需要在所有其他profile中显式复位:
server:
port: 8000
security:
user:
password: weak
24.6.4 YAML缺点
YAML文件不能通过@PropertySource注解加载。 因此,在需要以这种方式加载值的情况下,您需要使用properties文件。
24.6.5 合并YAML集合
如上所述( 在spring环境中将YAML暴露为properties), 任何YAML内容最终都会转换为property。当通过配置文件覆盖"list"属性时,该过可能跟你相信中的不一样.
例如,假设MyPojo对象的name和description属性的默认值是null,让我们从FooProperties暴露一个MyPojo的list:
@ConfigurationProperties("foo")
public class FooProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
考虑以下配置:
foo:
list:
- name: my name
description: my description
---
spring:
profiles: dev
foo:
list:
- name: my another name
上面示例中,如果dev profile是激活状态,foo.list的name为my another name,description为null
24.7 类型安全的配置熟悉
使用@Value("${property}")注解注入配置熟悉又时可能很麻烦,特别是你想使用多个属性或者你的数据是分层的.Spring Boot提供了一种处理属性的替代方案,它可以管理强类型bean和验证应用配置.例如:
@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {
private String username;
private InetAddress remoteAddress;
// ... getters and setters
}
属性的绑定是通过标准的java bean的getter和setter来绑定的.就像spring mvc中.对于不可变类型或者那些直接从String类型强转过来的强制类型,只要它们被初始化,映射,map和数组就需要一个getter,但是不一定需要setter。 因为它们可以被binder改变.如果有一个setter,可以创建map,list和数组.map和list只能使用getter来扩展,而数组需要一个setter.如果嵌套pojo属性具有默认构造函数,或者构造函数可以接受从String强转的单个值,则也可以创建嵌套pojo属性(因此setter不是必须的)。有些人使用Project Lombok自动添加getter和setter。 想要了解更多@Value和@ConfigurationProperties之间的区别请参考这里
你还需要列出这些properties类注册到@EnableConfigurationProperties注解中:
@Configuration
@EnableConfigurationProperties(ConnectionProperties.class)
public class MyConfiguration {
}
当@ConfigurationProperties bean以这种方式注册时,bean将具有常规名称:
- ,其中<prefix>是@ConfigurationProperties注解中指定的Spring环境key的前缀, 是bean的全限定名。如果注解中没有提供任何前缀.则只使用bean的全限定名.上面实例中的bean名称将是connection-com.example.ConnectionProperties,假设ConnectionProperties位于com.example包中。
即使上面的配置将为ConnectionProperties创建一个常规bean,我们建议@ConfigurationProperties只处理environment,特别是不从上下文注入其他bean。 话虽如此,@EnableConfigurationProperties注释也自动应用于您的项目,以便从环境属性配置使用@ConfigurationProperties注解的任何现有bean。 你可以通过确保ConnectionProperties已经是一个bean来达到MyConfiguration的快捷方式:
这种配置方式对于SpringApplication外部YAML配置特别有效:
# application.yml
connection:
username: admin
remoteAddress: 192.168.1.1
# additional configuration as required
要使用@ConfigurationProperties bean,您只需以与任何其他bean相同的方式注入它们。
@Service
public class MyService {
private final ConnectionProperties connection;
@Autowired
public MyService(ConnectionProperties connection) {
this.connection = connection;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server();
this.connection.configure(server);
}
}
使用@ConfigurationProperties还允许您生成可供IDE使用的元数据文件。 有关详细信息,请参阅附录B,配置元数据附录。
24.7.1 第三方配置
除了使用@ConfigurationProperties注释一个类,还可以在public @Bean方法中使用它。 当您想要将属性绑定到控件之外的第三方组件时,这可能特别有用。 要从Environment属性配置bean,请将@ConfigurationProperties添加到其bean注册:
@ConfigurationProperties(prefix = "foo")
@Bean
public FooComponent fooComponent() {
...
}
使用foo前缀定义的任何属性将以与上面的ConnectionProperties示例类似的方式映射到FooComponent bean。
24.7.2 轻松绑定
Spring Boot使用一些放宽的规则将Environment属性绑定到@ConfigurationProperties bean,因此不需要在Environment属性名和bean属性名之间进行精确匹配。 其中有用的常见示例包括虚线分隔(例如,上下文路径绑定到contextPath)和大写(例如,PORT绑定到端口)环境属性。
例如,给定以下@ConfigurationProperties类:
@ConfigurationProperties(prefix="person")
public class OwnerProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
name属性可以使用以下这些:
Property | 注意 |
---|---|
person.firstName | 标准的驼峰法命名 |
person.first-name | 建议在.properties和.yml文件中使用 |
person.first_name | .properties和.yml文件的备用格式。 |
PERSON_FIST_NAME | 大写格式。 使用系统环境变量时推荐使用。 |
24.7.3 属性转换
当Spring绑定到@ConfigurationProperties bean时,Spring将尝试用外部程序属性强制转换为正确类型.如果你需要自定义类型转换.你可以提供ConversionService bean(使用bean id conversionService)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义转换器(将bean定义注释为@ConfigurationPropertiesBinding)。
由于非常早的在程序生命周期请求此bean,请务必限制ConversionService正在使用的依赖关系.通常你所需的任何依赖关系在创建时可能无法完全初始化.如果配置key不需要自定ConversionService,则可能需要重命名自定义ConversionService,并且只依赖于使用@ConfigurationPropertiesBinding限定的自定义转换器
24.7.4 @ConfigurationProperties验证
Spring Boot会尝试验证外部配置,默认情况下使用JSR-303(如果他在classpath上)。你可以简单的向你的@ConfigurationProperties类添加JSR-303 javax.validation约束注解
@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
为了验证嵌套属性的值,必须将关联字段注解为@Valid以触发其验证.例如,基于上述ConnectionProperties示例:
@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {
@NotNull
@Valid
private RemoteAddress remoteAddress;
// ... getters and setters
public static class RemoteAddress {
@NotEmpty
public String hostname;
// ... getters and setters
}
}
可以通过创建名为configurationPropertiesValidator的bean定义来添加自定义Spring验证器。@Bean方法应该声明为static.configuration properties验证器创建于应用程序生命周期中的早期阶段并声明@Bean方法允许静态创建bean,而不必实例化@Configuration类。这避免了可能早期实例化引起的一些问题.有一个属性验证示例,所以你可以看到如何设置他们
spring-boot-actuator模块包含了一个暴露所有@ConfigurationProperties bean的端点.只需要将web浏览器指向/configprops或者使用等效的JMX端点.请参考生产就绪功能部分
24.7.5 @ConfigurationProperties vs. @Value
@Value是容器的核心功能,它不提供与类型安全的属性配置相同的功能.下面总结了@ConfigurationProperties喝@Value支持的功能:
Feature | @ConfigurationProperties | @Value |
---|---|---|
轻松绑定(模糊匹配) | Yes | NO |
元数据支持 | YES | NO |
SpEL 表达式 | NO | YES |
虽然您可以在@Value中写入SpEL表达式,但不会从应用程序属性文件处理此类表达式。