24. 外部化配置

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

SpringBoot使用了一个非常特殊的PropertySource先后顺序,它被设计为允许对value重写。Properties按以下先后顺序考虑:

  1. Devtools设置的全局属性作用的主目录(当devtools是active状态时是~/.spring-boot-devtools.propertis目录)
  2. @TestPropertySource注解作用的tests目录.
  3. @SpringBootTest#properties注解属性作用的tests目录
  4. 命令行参数
  5. SPRING_APPLICATION_JSON属性的配置(嵌入在环境变量或系统属性中的内嵌json)
  6. ServletConfig初始化的参数
  7. ServletContext初始化的参数
  8. java:comp/env的JNDI属性
  9. java System属性(System.getProperties())
  10. OS环境变量
  11. 一个只有随机属性的RandomValuePropertySource。
  12. jar包外部的Profile-specific application properties配置(application- {profile} .properties和YAML变体)
  13. jar包内部的Profile-specific application properties配置(application- {profile} .properties和YAML变体)
  14. jar包外的application properties(application.properties和yaml variants)
  15. jar包内的application properties(application.properties和yaml variants)
  16. @Configuration注解类的@PropertySource注解
  17. 默认属性(使用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环境(先后顺序,后面的覆盖前面的):

  1. 当前目录的/config子目录
  2. 当前目录
  3. classpath的/config包
  4. 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表达式,但不会从应用程序属性文件处理此类表达式。

Copyright © www.gitbook.com/@herryZ 2016 all right reserved,powered by Gitbook该文件修订时间: 2017-01-06 08:13:12

results matching ""

    No results matching ""