SpringBoot属性配置

SpringBoot属性配置(V2.0.2)

Spring Boot使您可以外部化配置,以便可以在不同环境中使用相同的应用程序代码。您可以使用properties文件,YAML文件,环境变量和命令行参数来外部化配置。可以使用@Value批注将属性值直接注入到您的bean中,可以通过SpringEnvironment访问,也可以通过@ConfigurationProperties绑定属性到对象。

Spring Boot使用一个非常特殊的PropertySource顺序,按以下顺序序号值小的可以覆盖序号值大的属性:

  1. 主目录上的Devtools全局设置属性(在devtools处于活动状态时,为〜/ .spring-boot-devtools.properties
  2. 测试用到的@TestPropertySource注解
  3. 测试用到的@SpringBootTest#properties注解
  4. 命令行参数,eg: --debug name=zhangsan
  5. 来自SPRING_APPLICATION_JSON属性
  6. ServletConfig初始化参数
  7. ServletContext处室话参数
  8. JNDI属性java:comp/env
  9. java系统属性(System.getProperties)
  10. OS操作系统属性
  11. RandomValuePropertySource属性,只有random.*的属性
  12. jar包之外的(application-{profile}.properties或者yaml,yml)属性
  13. jar包内的(application-{profile}.properties或者yaml,yml)属性
  14. jar包之外的(application.properties或者yaml,yml)属性
  15. jar包内的(application.properties或者yaml,yml)属性
  16. @Configuration注解类上的@PropertySource注解的属性
  17. 默认属性设置(SpringApplication.setDefaultProperties设置)
SPRING_APPLICATION_JSON Properties

命令行中使用环境变量来提供SPRING_APPLICATION_JSON属性:

$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar

还可以在System属性中将JSON作为spring.application.json提供:

$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar

您还可以使用命令行参数来提供JSON,如以下示例所示:

$ java -jar myapp.jar --spring.application.json='{"name":"test"}'

您还可以将JSON作为JNDI变量提供,如下所示:

java:comp/env/spring.application.json
Random Properties

RandomValuePropertySource可用于注入随机值(例如,测试用例中)。它可以产生integers,longs,uuid或strings,如以下示例所示:

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值(,max)CLOSE,其中OPEN,CLOSE是任何字符,而value,max是整数。如果提供了max,则value是最小值,而max是最大值(不包括)。

Command Line Properties

默认情况下,SpringApplication将所有命令行选项参数(即以--开头的参数,例如--server.port = 9000)转换为property,并将其添加到Spring Environment中。如前所述,命令行属性始终优先于其他属性源。

如果您不希望将命令行属性添加到Environment,则可以使用SpringApplication.setAddCommandLineProperties(false)禁用它们。

Properties Files

SpringApplication从位于以下位置的application.properties文件中加载属性(也可以使用YAML(.yml)文件来替代.properties。),并将它们添加到Spring Environment中:

  1. 当前目录的/ config子目录(应该是待执行的jar包所处位置为当前目录)
  2. 当前目录(应该是待执行的jar包所处位置为当前目录)
  3. classpath/config子目录
  4. classpath根目录

以上按序号排序,序号小的加载的属性将会覆盖序号大的属性值

如果您不喜欢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环境变量,系统属性或命令行参数) )。

如果spring.config.location包含目录(而不是文件),则它们应以/结尾(并且在运行时,应在加载之前附加从spring.config.name生成的名称,包括profile-specific文件的文件名)。 spring.config.location中指定的文件按原样使用,不支持profile-specific的变体,并且被任何profile-specific的属性覆盖。

配置位置以相反的顺序搜索。默认情况下,配置的位置是classpath:/,classpath:/ config /,file:./,file:./ config /。结果搜索顺序如下:

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

使用spring.config.location配置自定义配置位置后,它们将替换默认位置。例如,如果spring.config.location配置为值classpath:/ custom-config /,file:./ custom-config /,则搜索顺序如下:

  1. file:./custom-config/
  2. classpath:custom-config/

当使用spring.config.additional-location配置自定义配置位置时,除默认位置外,还会使用它们。在默认位置之前搜索其他位置。例如,如果配置了classpath:/ custom-config /,file:./ custom-config /的其他位置,则搜索顺序将变为以下内容:

  1. file:./custom-config/
  2. classpath:custom-config/
  3. file:./config/
  4. file:./
  5. classpath:/config/
  6. classpath:/

通过此搜索顺序,您可以在一个配置文件中指定默认值,然后在另一个配置文件中有选择地覆盖这些值。您可以在默认位置之一的application.properties(或使用spring.config.name选择的其他任何基本名称)中为应用程序提供默认值。然后,可以在运行时使用自定义位置之一中的其他文件覆盖这些默认值。

Profile-specific Properties

除了application.properties文件,还可以使用以下命名约定来定义特定于配置文件的属性:application- {profile} .properties。如果没有设置活动配置文件,则环境具有一组默认配置文件(默认为[default])。换句话说,如果未显式激活任何概要文件,那么将加载来自application-default.properties的属性。

Profile-specific的属性是从与标准application.properties相同的位置加载的,Profile-specific的文件总是会覆盖非特定文件,无论特定于配置文件的文件是在打包jar的内部还是外部。

如果指定了多个配置文件,则采用后赢策略。例如,在通过SpringApplication API配置的配置文件之后,添加了spring.profiles.active属性指定的配置文件,因此具有优先权。

Placeholders in Properties

使用application.properties中的值时,它们会通过现有的Environment进行过滤,因此您可以参考以前定义的值(例如,从“系统”属性中)。

app.name=MyApp
app.description=${app.name} is a Spring Boot application
Using YAML Instead of Properties

YAMLJSON的超集,因此是一种用于指定层次结构配置数据的便捷格式。只要在类路径上具有SnakeYAML库(spring-boot-starter会自动提供SnakeYAML),SpringApplication类就会自动支持YAML作为属性的替代方法。

Loading YAML

Spring Framework提供了两个方便的类,可用于加载YAML文档。 YamlPropertiesFactoryBeanYAML作为Properties加载,而YamlMapFactoryBeanYAML作为Map加载。

例如,考虑以下YAML文档:

environments:
	dev:
		url: http://dev.example.com
		name: Developer Setup
	prod:
		url: http://another.example.com
		name: My Cool App

前面的示例将转换为以下属性:

environments.dev.url=http://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=http://another.example.com
environments.prod.name=My Cool App

YAML列表用[index]解引用器表示为属性键。例如,考虑以下YAML

my:
servers:
	- dev.example.com
	- another.example.com

前面的示例将转换为以下属性:

my.servers[0]=dev.example.com
my.servers[1]=another.example.com

要通过使用Spring BootBinder实用程序(@ConfigurationProperties所做的)绑定到类似的属性,您需要在类型为java.util.List(或Set)的目标bean中拥有一个属性,或者您需要提供一个setter或使用可变值对其进行初始化。例如,以下示例绑定到前面显示的属性:

@ConfigurationProperties(prefix="my")
public class Config {

	private List<String> servers = new ArrayList<String>();

	public List<String> getServers() {
		return this.servers;
	}
}
Exposing YAML as Properties in the Spring Environment

YamlPropertySourceLoader类可用于在Spring环境中将YAML公开为PropertySource。这样做可以让您使用@Value批注和占位符语法来访问YAML属性。

Multi-profile YAML Documents

您可以使用spring.profiles键在一个文件中指定多个特定于配置文件的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配置文件处于活动状态,则server.address属性为127.0.0.1。同样,如果production配置文件处于活动状态,则server.address属性为192.168.1.120。如果未启用developmentproduction配置文件,则该属性的值为192.168.1.100

如果在启动应用程序上下文时未明确激活任何活动,则默认配置文件将被激活。因此,在以下YAML中,我们为spring.security.user.password设置了一个值,该值仅在“default”配置文件中可用:

server:
  port: 8000
---
spring:
  profiles: default
  security:
    user:
      password: weak

而在以下示例中,始终设置密码是因为该密码未附加到任何配置文件,并且必须根据需要在所有其他配置文件中将其显式重置:

server:
  port: 8000
spring:
  security:
    user:
      password: weak

使用spring.profiles元素指定的配置可以选择使用!来否定。特点。如果为单个文档同时指定了否定的配置文件和否定的配置文件,则至少一个非否定的配置文件必须匹配,并且否定的配置文件不能匹配。

YAML Shortcomings

无法使用@PropertySource批注来加载YAML文件。因此,在需要以这种方式加载值的情况下,需要使用properties文件。

Type-safe Configuration Properties

使用@Value(“ $ {property}”)批注来注入配置属性有时会很麻烦,尤其是当您使用多个属性或数据本质上是分层的时。Spring Boot提供了一种使用属性的替代方法,该属性使强类型的Bean可以管理和验证应用程序的配置,如以下所示:

package com.example;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

	private boolean enabled;

	private InetAddress remoteAddress;

	private final Security security = new Security();

	public boolean isEnabled() { ... }

	public void setEnabled(boolean enabled) { ... }

	public InetAddress getRemoteAddress() { ... }

	public void setRemoteAddress(InetAddress remoteAddress) { ... }

	public Security getSecurity() { ... }

	public static class Security {

		private String username;

		private String password;

		private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

		public String getUsername() { ... }

		public void setUsername(String username) { ... }

		public String getPassword() { ... }

		public void setPassword(String password) { ... }

		public List<String> getRoles() { ... }

		public void setRoles(List<String> roles) { ... }

	}
}

前面的POJO定义了以下属性:

  • acme.enabled
  • acme.remote-address
  • acme.security.username
  • acme.security.password.
  • acme.security.roles

您还需要列出要在@EnableConfigurationProperties中注册的属性类,如以下示例所示:

@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

以这种方式注册@ConfigurationProperties Bean时,该Bean具有常规名称:-,其中是@ConfigurationProperties批注中指定的环境密钥前缀,而是的完全限定名称。bean。如果注释不提供任何前缀,则仅使用Bean的完全限定名称。 上例中的Bean名称为acme-com.example.AcmeProperties。

即使前面的配置为AcmeProperties创建了常规bean,我们也建议@ConfigurationProperties仅处理环境,尤其不要从上下文中注入其他bean。话虽如此,@EnableConfigurationProperties批注也会自动应用到您的项目中,以便从环境配置任何使用@ConfigurationProperties批注的现有bean。您可以通过确保AcmeProperties已经是一个bean来快捷配置MyConfiguration,如以下示例所示:

@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {

	// ... see the preceding example

}

这种配置样式与SpringApplication外部YAML配置特别有效,如以下示例所示:

# application.yml

acme:
	remote-address: 192.168.1.1
	security:
		username: admin
		roles:
		  - USER
		  - ADMIN

# additional configuration as required

要使用@ConfigurationProperties Bean,可以像使用其他任何Bean一样注入它们,如以下示例所示:

@Service
public class MyService {

	private final AcmeProperties properties;

	@Autowired
	public MyService(AcmeProperties properties) {
	    this.properties = properties;
	}

 	//...

	@PostConstruct
	public void openConnection() {
		Server server = new Server(this.properties.getRemoteAddress());
		// ...
	}

}
Third-party Configuration

除了使用@ConfigurationProperties注释类之外,您还可以在公共@Bean方法上使用它。当您要将属性绑定到控件之外的第三方组件时,这样做特别有用。

要从Environment属性配置Bean请将@ConfigurationProperties添加到其Bean注册中,如以下示例所示:

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
	...
}

用另一个前缀定义的任何属性都以类似于前面的AcmeProperties示例的方式映射到该AnotherComponent bean

Relaxed Binding

Spring Boot使用一些宽松的规则将Environment属性绑定到@ConfigurationProperties bean,因此Environment属性名称和bean属性名称之间不需要完全匹配。有用的常见示例包括破折号分隔的环境属性(例如,context-path绑定到contextPath)和大写的环境属性(例如PORT绑定到port)。

例如,考虑以下@ConfigurationProperties类:

@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {

	private String firstName;

	public String getFirstName() {
		return this.firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

}

在前面的示例中,可以全部使用以下属性名称:

PropertyNote
acme.my-project.person.first-nameKebab大小写,建议在.properties和.yml文件中使用。
acme.myProject.person.firstName标准驼峰式语法。
acme.my_project.person.first_name下划线表示法,是.properties和.yml文件中使用的另一种格式。
ACME_MYPROJECT_PERSON_FIRSTNAME大写格式,在使用系统环境变量时建议使用。

注释的前缀值必须为kebab(小写,并用-分隔,例如acme.my-project.person)。

Property SourceSimpleList
Properties Files驼峰方案,Kebab大小写或下划线表示法使用[]或逗号分隔值的标准列表语法
YAML Files驼峰方案,Kebab大小写或下划线表示法标准YAML列表语法或逗号分隔的值
Environment Variables以下划线作为定界符的大写格式。 _不应在属性名称中使用下划线括起来的数值,例如MY_ACME_1_OTHER = my.acme [1] .other
System properties驼峰方案,Kebab大小写或下划线表示法使用[]或逗号分隔值的标准列表语法

我们建议,如果可能,属性以小写的kebab格式存储,例如my.property-name = acme。

Merging Complex Types

如果在多个位置配置了列表,则通过替换整个列表来进行覆盖。

例如,假设MyPojo对象的名称和描述属性默认为空。下面的示例从AcmeProperties公开MyPojo对象的列表:

@ConfigurationProperties("acme")
public class AcmeProperties {

	private final List<MyPojo> list = new ArrayList<>();

	public List<MyPojo> getList() {
		return this.list;
	}

}

考虑以下配置:

acme:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

如果开发人员配置文件未处于活动状态,则AcmeProperties.list包含一个MyPojo条目,如先前所定义。但是,如果启用了开发配置文件,则该列表仍仅包含一个条目(名称为my another name,并且descriptionnull)。此配置不会将第二个MyPojo实例添加到列表中,并且不会合并项目。

在多个配置文件中指定列表时,将使用优先级最高的列表(并且仅使用那个列表)。考虑以下示例:

acme:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
---
spring:
  profiles: dev
acme:
  list:
    - name: my another name

在前面的示例中,如果开发人员配置文件处于活动状态,则AcmeProperties.list包含一个MyPojo条目(其名称为my,另一个名称,并描述为null)。对于YAML,逗号分隔列表和YAML列表均可用于完全覆盖列表的内容。

对于Map属性,可以绑定从多个来源绘制的属性值。但是,对于多个来源中的相同属性,将使用优先级最高的属性。下面的示例从AcmeProperties公开Map <String,MyPojo>

@ConfigurationProperties("acme")
public class AcmeProperties {

	private final Map<String, MyPojo> map = new HashMap<>();

	public Map<String, MyPojo> getMap() {
		return this.map;
	}

}

考虑以下配置:

acme:
  map:
    key1:
      name: my name 1
      description: my description 1
---
spring:
  profiles: dev
acme:
  map:
    key1:
      name: dev name 1
    key2:
      name: dev name 2
      description: dev description 2

如果开发人员配置文件未处于活动状态,则AcmeProperties.map包含一个键为key1的条目(名称为我的名字1,描述为我的描述1)。但是,如果启用了开发配置文件,则map包含两个条目,其中键为key1(名称为dev name 1,描述我的描述1)和key2(名称为dev name 2,描述dev的描述2) 。

Properties Conversion

Spring Boot绑定到@ConfigurationProperties bean时,它尝试将外部应用程序属性强制为正确的类型。如果需要自定义类型转换,则可以提供一个ConversionService bean(具有名为conversionService的bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义Converters(具有定义为@ConfigurationPropertiesBinding的bean定义)。

由于在应用程序生命周期中非常早就请求了此bean,因此请确保限制您的ConversionService使用的依赖项。通常,您需要的任何依赖项在创建时可能都没有完全初始化。如果配置键强制不需要自定义的转换服务,而仅依赖于具有@ConfigurationPropertiesBinding限定条件的自定义转换器,则可能要重命名自定义的转换服务。

Spring Boot为表达持续时间提供了专门的支持。如果公开java.time.Duration属性,则应用程序属性中的以下格式可用:

  • 常规的long表示形式(使用毫秒作为默认单位,除非已指定@DurationUnit
  • java.util.Duration使用的标准ISO-8601格式
  • 值和单位相结合的更易读的格式(例如10s表示10秒)

考虑以下示例:

@ConfigurationProperties("app.system")
public class AppSystemProperties {

	@DurationUnit(ChronoUnit.SECONDS)
	private Duration sessionTimeout = Duration.ofSeconds(30);

	private Duration readTimeout = Duration.ofMillis(1000);

	public Duration getSessionTimeout() {
		return this.sessionTimeout;
	}

	public void setSessionTimeout(Duration sessionTimeout) {
		this.sessionTimeout = sessionTimeout;
	}

	public Duration getReadTimeout() {
		return this.readTimeout;
	}

	public void setReadTimeout(Duration readTimeout) {
		this.readTimeout = readTimeout;
	}

}

要指定30秒的会话超时,则30,PT30S和30s都是等效的。可以使用以下任意形式指定500ms的读取超时:500,PT0.5S和500ms。

您也可以使用任何受支持的单元。这些都是:

  • ns for nanoseconds
  • ms for milliseconds
  • s for seconds
  • m for minutes
  • h for hours
  • d for days

默认单位是毫秒,可以使用@DurationUnit覆盖,如上面的示例所示。

@ConfigurationProperties Validation

每当使用Spring的@Validated批注对@ConfigurationProperties类进行批注时,Spring Boot就会尝试对其进行验证。您可以在配置类上直接使用JSR-303 javax.validation约束注释。为此,请确保您的类路径上有兼容的JSR-303实现,然后将约束注释添加到您的字段中,如以下示例所示:

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

	@NotNull
	private InetAddress remoteAddress;

	// ... getters and setters

}

您还可以通过使用@Validated注释创建配置属性的@Bean方法来触发验证。

尽管嵌套属性也将在绑定时进行验证,但最好还是将关联的字段注释为@Valid。即使没有找到嵌套的属性,这也可以确保验证被触发。以下示例以前面的AcmeProperties示例为基础:

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

	@NotNull
	private InetAddress remoteAddress;

	@Valid
	private final Security security = new Security();

	// ... getters and setters

	public static class Security {

		@NotEmpty
		public String username;

		// ... getters and setters

	}

}

您还可以通过创建一个名为configurationPropertiesValidator的bean定义来添加自定义Spring Validator@Bean方法应声明为静态。配置属性验证器是在应用程序生命周期的早期创建的,并且将@Bean方法声明为static可以使创建该Bean而不必实例化@Configuration类。这样做避免了由早期实例化引起的任何问题。有一个属性验证示例,显示了如何进行设置。

@ConfigurationProperties vs. @Value

@Value批注是核心容器功能,它没有提供与类型安全的配置属性相同的功能。下表总结了@ConfigurationProperties和@Value支持的功能:

Feature@ConfigurationProperties@Value
Relaxed bindingYesNo
Meta-data supportYesNo
SpEL evaluationNoYes

如果您为自己的组件定义了一组配置键,我们建议您将它们组合在以@ConfigurationProperties注释的POJO中。您还应该意识到,由于@Value不支持宽松的绑定,因此如果您需要使用环境变量来提供值,则它不是一个很好的选择。

最后,尽管您可以在@Value中编写SpEL表达式,但不会从应用程序属性文件中处理此类表达式。

参考文献

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值