zoukankan      html  css  js  c++  java
  • 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
      srcmain esourcesconfig
    4. 类路径
      srcmain esources

    可以使用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文件,还有编写配置文件时的提示功能。

  • 相关阅读:
    Selenium生成Report的利器- ExtentReports
    学习使用monkey 测试
    charles 结合mocky 模拟数据
    Vue.use()源码分析且执行后干什么了
    commonjs 与 es6相关Module语法的区别
    vue函数化组件 functional
    html5细线表格制作
    移动端H5页面禁止长按复制和去掉点击时高亮
    javascript生成器
    promise和生成器的结合
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/11831634.html
Copyright © 2011-2022 走看看