20191110 Spring Boot官方文档学习(4.2)

4.2。外部化配置

Spring Boot使您可以外部化配置,以便可以在不同环境中使用相同的应用程序代码。您可以使用Properties文件YAML文件环境变量命令行参数来外部化配置。属性值可以通过@Value注解直接注入到你的bean ,通过Spring Environment抽象的访问,或者通过@ConfigurationProperties绑定到结构化对象。

Spring Boot使用一个非常特殊的PropertySource顺序,该顺序旨在允许合理地覆盖值。属性按以下顺序考虑,优先级从高到低:

  1. devtools处于活动状态时,文件夹$HOME/.config/spring-boot中的Devtools全局设置属性。
  2. tests中的@TestPropertySource注解。
  3. tests中的properties属性。可用于@SpringBootTest注解和测试应用程序的特定部分的测试注解。
  4. 命令行参数。
  5. 来自SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的嵌入式JSON)的属性。
  6. ServletConfig 初始化参数。
  7. ServletContext 初始化参数。
  8. java:comp/env的JNDI属性。
  9. Java系统属性(System.getProperties())。
  10. 操作系统环境变量。
  11. 只有在有 random.* 属性时的RandomValuePropertySource
  12. 打包的jar之外的 Profile-specific application propertiesapplication-{profile}.properties和YAML变体)。
  13. 打包在jar中的特定于配置文件的应用程序属性(application-{profile}.properties和YAML变体)。
  14. 打包的jar之外的应用程序属性(application.properties和YAML变体)。
  15. 打包在jar中的应用程序属性(application.properties和YAML变体)。
  16. @Configuration类上的@PropertySource注解。
  17. 默认属性(通过设置SpringApplication.setDefaultProperties指定)。

注意
打印null:

@Controller
public class DemoController {

    @Value("${myname}")
    private String myname;

    public DemoController(){
        System.out.println(myname);
    }
}

打印配置的值:

@Controller
public class DemoController {

    public DemoController(@Value("${myname}") String myname){
        System.out.println(myname);
    }
}

使用SPRING_APPLICATION_JSON属性:

  1. UN*X shell中:
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
  1. System属性
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
  1. 命令行参数
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
  1. 将JSON作为JNDI变量提供
java:comp/env/spring.application.json

4.2.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(10) 表示小于10的整数

4.2.2。访问命令行属性

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

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

4.2.3。应用程序属性文件

SpringApplication从以下位置的application.properties文件中加载属性并将其添加到Spring Environment中,优先级从高到低:

  1. 当前目录的/config子目录
    jar包所在目录的config目录下
  2. 当前目录
    jar包所在目录
  3. 类路径的/config
    src\main\resources\config
  4. 类路径
    src\main\resources

可以使用YAML(.yml)文件来替代.properties

spring.config.name 属性指定配置文件名称(默认application.properties),spring.config.location 属性指定配置文件位置(逗号分隔多个目录或文件)

spring.config.name=myproject
spring.config.location=classpath:/default.properties,classpath:/override.properties

spring.config.namespring.config.location很早就用于确定必须加载哪些文件。必须将它们定义为环境属性(通常是OS环境变量,系统属性或命令行参数)。

如果spring.config.location包含目录(而不是文件),则应以 / 结尾(并且在运行时,在目录后附加从 spring.config.name 加载之前生成的名称,包括 profile-specific 文件名)。spring.config.location 指定的文件按原样使用,不支持特定于配置文件的变体,并且被任何 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:/

如果您使用环境变量而不是系统属性,则大多数操作系统都不允许使用句点分隔的键名,但是您可以使用下划线代替(例如,SPRING_CONFIG_NAME代替spring.config.name)。

如果您的应用程序在容器中运行,则可以使用JNDI属性(在 java:comp/env 中)或Servlet上下文初始化参数来代替环境变量或系统属性。

4.2.4。Profile-specific 属性

application.properties文件外,还可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.properties。如果没有 active 的 profile,在Environment具有一组默认的profile(默认[default])。换句话说,如果没有 active 的 profile,那么将从application-default.properties中加载属性。

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

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

如果您在 spring.config.location 中指定了任何文件,则不会考虑profile-specific 的配置文件。如果您还想使用profile-specific 的配置文件,请使用spring.config.location 指定目录。

4.2.5。属性中的占位符

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

app.name=MyApp
app.description=${app.name} is a Spring Boot application

4.2.6。加密属性

Spring Boot不提供对加密属性值的任何内置支持,但是,它提供了修改Spring Environment包含的值所必需的挂钩点。EnvironmentPostProcessor接口允许您在应用程序启动之前操作Environment。

4.2.7。使用YAML代替Properties

YAML是JSON的超集,是一种用于指定层次结构配置数据的便捷格式。SpringApplication自动支持YAML,只要SnakeYAML库在classpath中。

spring-boot-starter 自带 SnakeYAML

加载YAML

Spring Framework提供了两个方便的类,可用于加载YAML文档。YamlPropertiesFactoryBean加载YAML为PropertiesYamlMapFactoryBean加载YAML为Map

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

等同于

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

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

@ConfigurationProperties(prefix = "my")
public class MyConfig {
    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

在Spring Environment中将YAML配置文件为属性

YamlPropertySourceLoader类可用于在Spring Environment暴露YAML作为PropertySource。这样做使您可以将@Value注解和占位符语法一起使用以访问YAML属性。

多Profile的YAML文档

在一个YAML文档中可以使用 spring.profiles 键来指示属性何时适用,从而文件中指定多个 profile ,如以下示例所示:

server:
  address: 192.168.1.100
---
spring:
  profiles: development
server:
  address: 127.0.0.1
---
spring:
  profiles: production & eu-central
server:
  address: 192.168.1.120

在此示例中,如果development profile处于active状态,则server.address属性为127.0.0.1。同样,如果production 和 eu-central profiles处于active状态,则server.address属性为192.168.1.120。如果development,production和eu-central在配置文件没有启用,那么该属性的值192.168.1.100。

如果在启动应用程序上下文时未明确active任何profile,则会激活 default profile。因此,在以下YAML中,我们设置了一个仅在default profile 中可用的值 spring.security.user.password:

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

spring.profiles 可以使用简单的表达式逻辑。

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

YAML的缺点

无法通过使用@PropertySource注解加载YAML文件。

在特定于profile的YAML文件中使用多YAML文档语法可能会导致意外行为。

# 文件名application-dev.yml
server:
    port: 8000
---
spring:
    profiles: "!test"
    security:
        user:
            password: "secret"

当激活dev profile时,spring.security.user.password不是"secret",因为YAML文档已被指定为dev profile,所以会忽略嵌套的profile配置。

不要混用指定profile的YAML文件和多 profiles YAML文档。坚持只使用其中之一。

4.2.8。类型安全的配置属性

JavaBean属性绑定

yml配置:

acme:
  enabled: true
  remoteAddress: 10.1.1.2
  security:
    username: abc
    password: xxx
    roles:
      - a
      - b
      - c
      - x

Java注入类:

@ConfigurationProperties("acme")
@Data
public class AcmeProperties {
    private boolean enabled;
    private InetAddress remoteAddress;
    private final Security security = new Security();

    @Data
    public static class Security {
        private String username;
        private String password;
        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
    }
}

acme.security.roles:其默认值为内容为USER的集合。

Spring Boot自动配置大量使用@ConfigurationProperties来轻松配置自动配置的Bean。与自动配置类相似,Spring Boot中可用的@ConfigurationProperties类仅供内部使用。通过属性文件,YAML文件,环境变量等配置的映射到类的属性是公共API,但是并不意味着类本身的内容可以直接使用。

这种安排依赖于默认的空构造器,并且gettersetter通常是强制性的,因为绑定是通过标准Java Beans属性描述符进行的,就像在Spring MVC中一样。在以下情况下,可以省略setter方法:

  • 只要Map被初始化,它们就需要使用getter,但不一定需要使用setter,因为它们可以被binder改变。
  • 可以通过索引(YAML)或使用单个逗号分隔的值(properties)来访问集合和数组。在后一种情况下,必须使用setter。我们建议始终为此类类型添加setter。如果初始化集合,请确保它不是不可变的(如上例所示)。
  • 如果嵌套的POJO属性已初始化(如前面示例中的Security字段),则不需要setter。如果希望通过binder使用其默认构造函数动态创建实例,则需要一个setter。

有些人使用Lombok自动添加setter和getter。确保Lombok不会为这种类型生成任何特定的构造函数,因为容器会自动使用它来实例化该对象。

最后,仅考虑标准Java Bean属性,不支持对静态属性的绑定

我的发现

使用@ConfigurationProperties注解,IDEA出现警告信息:
image

解决方法:
加入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

作用:
点击自定义配置时,可以跳转到Java类上,也可以在编写配置文件时进行提示,但是提示功能要运行一次程序。

构造函数绑定

@ConstructorBinding注解指示应使用构造函数绑定。@ConstructorBinding类的嵌套成员(例如Security)也将通过其构造函数进行绑定。

可以使用@DefaultValue指定默认值,并且将应用相同的转换服务将String值强制转为缺少属性的目标类型。

import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;

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

@ToString
@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {
    private boolean enabled;
    private InetAddress remoteAddress;
    private final Security security;

    public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        System.out.println("AcmeProperties...AcmeProperties...");
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }


    @ToString
    public static class Security {
        private String username;
        private String password;
        private List<String> roles;

        public Security(String username, String password,
                @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }
    }
}

如果您的类具有多个构造函数,则还可以直接在应绑定的构造函数上使用@ConstructorBinding

要使用构造函数绑定,必须使用@EnableConfigurationProperties或配置属性扫描(@SpringBootApplication带有@ConfigurationPropertiesScan,即属性扫描。)。您不能对通过常规Spring机制创建的bean使用构造函数绑定(例如,@Component beans,通过@Bean方法创建的beans或使用@Import加载的bean)

启用@ConfigurationProperties注解的类

Spring Boot提供了绑定类型并将其自动注册为Bean的基础架构。如果您的应用程序使用@SpringBootApplication,带有@ConfigurationProperties注解的类将被自动扫描并注册为bean。默认情况下,扫描将从声明@SpringBootApplication注解的类的包中进行。如果要定义要扫描的特定程序包,可以在@SpringBootApplication注解类上使用显式指令@ConfigurationPropertiesScan来进行扫描,如以下示例所示:

@SpringBootApplication
@ConfigurationPropertiesScan({"com.example.app", "org.acme.another"})
public class MyApplication {

有时,带@ConfigurationProperties注解的类可能不适用于扫描(或不在制定的扫描中),例如,如果您正在开发自己的自动配置。在这些情况下,您可以手动指定要在任何@Configuration类上处理的类型的列表,如以下示例所示:

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

@ConfigurationProperties bean使用配置属性扫描或通过@EnableConfigurationProperties注册,bean具有常规名称:<prefix>-<fqn>,其中,<prefix>是在@ConfigurationProperties注解中指定的环境键前缀,<fqn>是bean的全限定名。如果注解不提供任何前缀,则仅使用Bean的完全限定名称。
上例中的Bean名称为acme-com.example.AcmeProperties

我们建议@ConfigurationProperties仅处理环境,尤其不要从上下文中注入其他bean。对于特殊情况,可以使用setter注入或框架提供的*Aware接口(例如,如果需要访问Environment,EnvironmentAware)。如果您仍想使用构造函数注入其他bean,则必须对配置属性bean使用@Component注释,并使用基于JavaBean的属性绑定。

第三方配置

@ConfigurationProperties除了用于注释类之外,您还可以在公共@Bean方法上使用它。

@Configuration
public class MyConfiguration {
    @ConfigurationProperties("acme")
    @Bean
    public AcmeProperties acmeProperties() {
        return new AcmeProperties();
    }
}

这种方式适用于不能修改源码的时候配置Bean。

宽松绑定

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

示例:

@Data
@ConfigurationProperties(prefix = "acme.my-project.person")
public class OwnerProperties {
    private String firstName;
}

属性文件中可以使用以下属性绑定:

属性 注意
acme.my-project.person.first-name 短横线式(Kebab),建议在.properties和.yml文件中使用。
acme.myProject.person.firstName 标准驼峰式(Camel)语法。
acme.my_project.person.first_name 下划线(Underscore)表示法,是在.properties和.yml文件中使用的另一种格式。
ACME_MYPROJECT_PERSON_FIRSTNAME 大写格式,使用系统环境变量时建议使用。

@ConfigurationProperties的属性prefix的值必须为短横线式(小写并用分隔-,例如acme.my-project.person)。不管配置文件中是my-project还是myProject。

宽松绑定的规则:

属性来源 Simple List
Properties文件 驼峰式,短横线式或下划线式 使用[ ]或以逗号分隔的值的标准列表语法
YAML文件 驼峰式,短横线式或下划线式 标准YAML List语法或逗号分隔的值
环境变量 以下划线作为分隔符的大写格式。_不应在属性名称中使用 下划线括起来的数值,例如MY_ACME_1_OTHER = my.acme[1].other
系统属性 驼峰式,短横线式或下划线式 使用[]或以逗号分隔的值的标准列表语法

我们建议,属性以小写短横线式存储,例如my.property-name=acme

绑定Map属性时,如果key包含除小写字母、数字字符或 - 之外的任何内容,则需要使用方括号表示法,以便保留原始值。如果键没有被 [] 包围,则所有其他字符被删除。例如:

map:
  "[/key1]": value1
  "[/key2]": value2
  /key3: value3

Map具有 /key1/key2key3 作为映射中的键。

合并复杂类型

ListMap在多个 Profiles 中指定时,将使用优先级最高的一个(并且只有该优先级)。

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

如果dev profile未激活,则AcmeProperties.list包含一个MyPojo条目。但是,如果激活了dev profile,则list仍然仅包含一个条目(名称为my another name,description为null)。此配置不会将第二个MyPojo实例添加到列表中,并且不会合并项目。

属性转换

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

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

转换时间

Spring Boot为表达 durations 提供了专门的支持。如果定义了java.time.Duration属性,则配置文件中属性可用以下格式:

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

考虑以下示例:

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

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

    private Duration readTimeout = Duration.ofMillis(1000);

}

要指定30秒的 sessionTimeout ,30、PT30S、30s都等效。500毫秒的 readTimeout 可以以任何形式如下指定:500,PT0.5S和500ms。

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

  • ns 纳秒
  • us 微秒
  • ms 毫秒
  • s 秒
  • m 分钟
  • h 小时
  • d 天

默认时间单位是毫秒,可以使用@DurationUnit注解指定时间单位。

转换数据大小

Spring Framework的DataSize值类型表示字节大小。如果定义DataSize属性,则配置文件中属性可用以下格式:

  • 常规long表示形式(除非@DataSizeUnit已指定,否则使用 byte 作为默认单位)
  • 值和单位耦合的更具可读性的格式(例如,10MB意味着10兆字节)

考虑以下示例:

@Data
@ConfigurationProperties("app.io")
public class AppIoProperties {
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);

    private DataSize sizeThreshold = DataSize.ofBytes(512);

}

指定10 MB的bufferSize,10、10MB等效。可以将256个字节的 sizeThreshold 指定为256或256B。

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

  • B 字节(byte)
  • KB 千字节(K byte)
  • MB 兆字节(M byte)
  • GB 千兆字节(G byte)
  • TB 太字节(T byte)

默认单位是字节(byte),可以使用@DataSizeUnit指定单位。

@ConfigurationProperties验证

Spring Boot会验证带有@Validated注解的@ConfigurationProperties类。您可以直接在配置类上使用JSR-303 javax.validation 约束注解。为此,请确保在类路径上有兼容的JSR-303实现(例如,spring-boot-starter-validation),然后将约束注解添加到字段上,如以下示例所示:

@Data
@ConfigurationProperties(prefix = "acme")
@Validated
public class AcmeProperties {
    @NotNull
    private InetAddress remoteAddress;
}

还可以通过使用@Validated注解@Bean方法来触发验证。

尽管绑定时也会验证嵌套属性,但最好在嵌套属性上使用@Valid注解。这样可确保即使未找到嵌套属性也将触发验证。下面的示例基于前面的AcmeProperties示例:

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

    @NotNull
    private InetAddress remoteAddress;

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

    @Data
    public static class Security {
        @NotEmpty
        private String username;
        private String password;
        private List<String> roles;
    }
}

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

spring-boot-actuator模块包括一个公开所有@ConfigurationPropertiesbean 的功能。将您的Web浏览器指向/actuator/configprops或使用等效的JMX端点。需要添加依赖并配置属性:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 开启所有Endpoint
management.endpoints.web.exposure.include=*

@ConfigurationProperties@Value

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

功能 @ConfigurationProperties @Value
宽松绑定
元数据支持
SpEL 表达式

元数据支持即,从配置文件跳转到Java文件,还有编写配置文件时的提示功能。

posted @ 2019-11-10 20:17  流星<。)#)))≦  阅读(381)  评论(0编辑  收藏  举报