[TOC] 应用配置,在官方文档也称为配置外部化,意思就是在不同的环境中使用相同的程序代码,也就是将代码和配置进行分离。 Spring Boot支持多种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。配置的属性值可以通过使用 @Value 注释直接注入到你的 bean 中,通过 Spring 的 Environment 抽象访问; 也可以通过 @ConfigurationProperties 绑定到结构化对象。 ## 属性配置的优先级 Spring Boot 使用了一个非常特殊的 PropertySource 顺序,该顺序旨在允许合理地覆盖值。 属性按以下顺序考虑(后面的覆盖前面的): 1. 默认配置: 使用 `SpringApplication.setDefaultProperties` 指定 2. @Configuration 类上的 @PropertySource 注释。 请注意,在刷新应用程序上下文之前,此类属性源不会添加到环境中。 配置某些属性(例如 logging.* 和 spring.main.* 在刷新开始之前读取)就晚了。 3. 在application.properties 等配置文件的配置数据 4. 在random.*. 中的属性。 RandomValuePropertySource 5. OS环境变量 6. Java系统变量 (System.getProperties()) 7. 在java:comp/env 中的JNDI属性 8. ServletContext 中的参数 9. ServletConfig 中的参数 10. SPRING_APPLICATION_JSON 的属性(嵌入在环境变量或系统属性中的内联 JSON) 11. 命令行参数 12. 测试的属性。 在@SpringBootTest 和测试注解上可用,用于测试应用程序的特定部分。 13. 测试上的@TestPropertySource 14. 当 devtools 处于活动状态时,$HOME/.config/spring-boot 目录中的 Devtools 全局设置属性。 配置数据文件按以下顺序考虑: 1. 打包在 jar 中的应用程序属性(application.properties 和 YAML 变体)。 2. 打包在 jar 中的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML 变体)。 3. 打包 jar 之外的应用程序属性(application.properties 和 YAML 变体)。 4. 打包 jar 之外的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML 变体)。 注意: 配置文件支持 .properties 和 .yml 两种格式, 如果两种格式的文件都有,则优先以 .properties 为准。 举例来看, 假设开发了一个使用 name 属性的 @Component,如以下所示: ``` @Component public class MyBean { @Value("${name}") private String name; // ... } ``` 在应用程序类路径上(例如,在 jar 中)您可以拥有一个 application.properties 文件,该文件为 name 提供了一个合理的默认属性值。 在新环境中运行时,可以在 jar 之外提供一个 application.properties 文件来覆盖名称。 对于一次性测试,可以使用特定的命令行开关启动(例如,java -jar app.jar --name="Spring")。在命令行参数可以覆盖这个配置 `java -jar app.jar --name="Spring"` 提示: env 和 configprops 端点可用于确定属性为何具有特定值。 您可以使用这两个端点来诊断意外的属性值。 ## 1. 获取命令行属性 默认情况下,SpringApplication 将任何命令行选项参数(即以 -- 开头的参数,例如 `--server.port=9000`)转换为属性并将它们添加到 Spring Environment。 如前所述,命令行属性始终优先于基于文件的属性源。 如果不希望将命令行属性添加到环境中,可以使用`SpringApplication.setAddCommandLineProperties(false)`禁用。 ## 2. JSON格式应用属性 环境变量和系统属性通常有限制,这意味着某些属性名称不能使用。 为了解决这个问题,Spring Boot 允许将一组属性编码为JSON 格式。 应用程序启动时,任何 spring.application.json 或 SPRING_APPLICATION_JSON 属性都将被解析并添加到环境中。 举例来看, SPRING_APPLICATION_JSON 属性可以在 UN*X shell 的命令行中作为环境变量提供: `.$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar` 将JSON作为系统属性 `java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar` 将JSON作为命令行参数: `$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'` 如果您要部署到经典应用程序服务器,配置JNDI 变量 `java:comp/env/spring.application.json` 注意: PropertySourcesPropertyResolver 将null 作为缺省值,所以null 值无法进行覆盖。 ## 3. 外部应用程序属性 应用程序启动时,Spring Boot 会自动从以下位置查找并加载 application.properties 和 application.yaml 文件: 1. 从类路径 - 类根路径 -类路径 `/conifg` 目录 2. 从当前路径 - 当前路径 -当前路径 /config 子目录 - /config 子目录的直接子目录 该列表按优先级排序(较低项目的值覆盖较早的项目)。 来自加载文件的文档作为 PropertySource 添加到 Spring Environment。 如果不喜欢 application 作为配置文件名,可以通过指定 spring.config.name 环境属性来切换到另一个文件名。 例如,要查找 myproject.properties 和 myproject.yaml 文件,可以按如下方式运行应用程序: `java -jar myproject.jar --spring.config.name=myproject` 还可以使用 spring.config.location 环境属性来引用显式位置。 此属性接受一个或多个要检查的位置的逗号分隔列表。 以下示例显示了如何指定两个不同的文件: ``` java -jar myproject.jar --spring.config.location=\ optional:classpath:/default.properties,\ optional:classpath:/override.properties ``` 提示:使用前缀optional:如果位置是可选的并且不介意它们不存在。 特别注意: spring.config.name、spring.config.location 和 spring.config.additional-location 很早就用于确定必须加载哪些文件。 它们必须定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。 如果 spring.config.location 包含目录(而不是文件),它们应该以 / 结尾。 在运行时,它们将在加载之前附加从 spring.config.name 生成的名称。 spring.config.location 中指定的文件是直接导入的。 注意: 目录和文件位置值也被扩展以检查特定于配置文件的文件。 例如,如果有一个 spring.config.location 的 classpath:myconfig.properties,您还会发现加载了相应的 classpath:myconfig-<profile>.properties 文件。 大多数情况下,添加的每个 spring.config.location 项都将引用单个文件或目录。 位置按照定义的顺序进行处理,后面的位置可以覆盖前面位置的值。 如果位置设置的比较复杂,并且使用特定profile的配置文件,可以进行位置分组。 位置组是所有被视为同一级别的位置的集合。 例如,对所有类路径位置进行分组,然后对所有外部位置进行分组。 位置组中的项目应以 ; 分隔。 有关更多详细信息,请参阅“特定Profile的文件”部分。 使用 spring.config.location 配置的位置替换默认位置。 例如,如果使用值 optional:classpath:/custom-config/,optional:file:./custom-config/ 配置 spring.config.location,则考虑的完整位置集是: 1. `optional:classpath:custom-config/` 2. `optional:file:./custom-config/` 如果需要添加其他位置,而不是替换它们,可以使用 spring.config.additional-location。 从其他位置加载的属性可以覆盖默认位置中的属性。 例如,如果 spring.config.additional-location 配置了值 optional:classpath:/custom-config/,optional:file:./custom-config/,则考虑的完整位置集是: 1. `optional:classpath:/;optional:classpath:/config/` 2. `optional:file:./;optional:file:./config/;optional:file:./config/*/` 3. `optional:classpath:custom-config/` 4. `optional:file:./custom-config/` 这种搜索顺序允许在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。 可以在默认位置之一的 application.properties(或您使用 spring.config.name 选择的任何其他基本名称)中为应用程序提供默认值。 然后可以在运行时使用位于自定义位置之一的不同文件覆盖这些默认值。 注意: 如果使用环境变量而不是系统属性,大多数操作系统不允许使用句点分隔的键名,但可以使用下划线代替(例如,SPRING_CONFIG_NAME 而不是 spring.config.name)。 有关详细信息,请参阅从环境变量绑定。 注意:如果应用程序在 servlet 容器或应用程序服务器中运行,则可以使用 JNDI 属性(在 java:comp/env 中)或 servlet 上下文初始化参数来代替或同时使用环境变量或系统属性。 #### 1. 可选位置 optional 默认情况下,当指定的配置数据位置不存在时,Spring Boot 将抛出 ConfigDataLocationNotFoundException 并且应用程序不会启动。 使用 `optional:` 前缀指定的配置路径, 则该路径的文件不存在也可以启动, 该前缀可以和spring.config.location spring.config.additional-location 以及 spring.config.import 一起使用。 举例来看: spring.config.import 的值指定为 `optional:file:./myconfig.properties`, 则myconfig.properties文件不存在也可以启动。 使用spring.config.on-not-found 属性可以设置忽略所有 ConfigDataLocationNotFoundExceptions 异常, 使应用一直可以启动。 设置方式是通过 `SpringApplication.setDefaultProperties(…​)` 或者系统/环境变量设置这个属性的值为 `ignore ` 。 #### 2. 通配符位置 如果配置文件位置包含最后一个路径段的 * 字符,则将其视为通配符位置。 加载配置时会扩展通配符,以便还检查直接子目录。 当有多个配置属性源时,通配符位置在 Kubernetes 等环境中特别有用。 例如,如果有一些 Redis 配置和一些 MySQL 配置,希望将这两个配置分开,同时要求它们都存在于 application.properties 文件中。 这可能会导致两个单独的 application.properties 文件挂载在不同的位置,例如 /config/redis/application.properties 和 /config/mysql/application.properties。 在这种情况下,通配符位置 config/*/ 将导致两个文件都被处理。 比如 Redis 配置文件 `/config/redis/application.properties` MySQL的配置文件 `/config/mysql/application.properties`。 使用 `spring.config.location ` 或` spring.config.additional-location` 配置 `config/*/` 。 默认情况下,Spring Boot 在默认搜索位置包含 config/*/ 。 这意味着将搜索 jar 之外的 /config 目录的所有子目录。 可以自己使用带有 spring.config.location 和 spring.config.additional-location 属性的通配符位置。 注意: 通配符位置必须仅包含一个 * 并以 */ 结尾,表示搜索位置为目录,或以 */<filename> 结尾,表示搜索位置为文件。 带有通配符的位置根据文件名的绝对路径按字母顺序排序。 提示:通配符路径仅在外部路径,不能在 `classpath` 路径中使用。 #### 3 特定环境的配置文件 除了应用程序属性文件,Spring Boot 还将尝试使用命名约定 application-{profile} 加载特定于配置文件的文件。 例如,如果应用程序激活名为 prod 的配置文件并使用 YAML 文件,则 application.yml 和 application-prod.yml 都将被考虑。 特定于配置文件的属性从与标准 application.properties 相同的位置加载,特定于配置文件的文件始终覆盖非特定文件。 如果指定了多个配置文件,则采用最后获胜的策略。 例如,如果配置文件 prod,live 由 spring.profiles.active 属性指定,则 application-prod.properties 中的值可以被 application-live.properties 中的值覆盖。 注意: 最后获胜策略适用于位置组级别。 classpath:/cfg/,classpath:/ext/ 的 spring.config.location 不会与 classpath:/cfg/;classpath:/ext/ 具有相同的覆盖规则。 例如,继续上面的 prod,live 示例,可能有以下文件: ~~~ /cfg application-live.properties /ext application-live.properties application-prod.properties ~~~ 当有 classpath:/cfg/,classpath:/ext/ 的 spring.config.location 时,在所有 /ext 文件之前处理所有 /cfg 文件: 1. `/cfg/application-live.properties` 2. `/ext/application-prod.properties` 3. `/ext/application-live.properties` 当使用 classpath:/cfg/;classpath:/ext/ 代替(使用 ; 分隔符)时,在同一级别处理 /cfg 和 /ext: 1. `/ext/application-prod.properties` 2. `/cfg/application-live.properties` 3. `/ext/application-live.properties` 环境有一组默认配置文件(默认情况下使用[default]),如果没有设置活动配置文件,则使用这些配置文件。 换句话说,如果没有明确激活配置文件,则考虑来自 application-default 的属性。 注意:属性文件只加载一次。 如果已经直接导入了配置文件特定的属性文件,则不会再次导入。 #### 4. 导入附加数据 应用程序属性可以使用 spring.config.import 属性从其他位置导入更多配置数据。 进口在被发现时进行处理,并被视为附加文件,紧接在声明进口的文件之下。 例如, application.properties 文件中可能包含以下内容: ``` spring: application: name: "myapp" config: import: "optional:file:./dev.properties" ``` 这将触发在当前目录中导入 dev.properties 文件(如果存在这样的文件)。 导入的 dev.properties 中的值将优先于触发导入的文件。 在上面的示例中, dev.properties 可以将 spring.application.name 重新定义为不同的值。 一个导入无论声明多少次都只会被导入一次。 在 properties/yaml 文件中的单个文档中定义导入的顺序无关紧要。 例如,下面的两个示例产生相同的结果: ``` spring: config: import: my.properties my: property: value ``` ``` my: property: value spring: config: import: my.properties ``` 在上述两个示例中,来自 my.properties 文件的值将优先于触发其导入的文件。 可以在单个 spring.config.import 键下指定多个位置。 位置将按照定义的顺序进行处理,以后的导入优先。 注意:还考虑导入特定于配置文件的变体。 上面的示例将同时导入 my.properties 以及任何 my-<profile>.properties 变体。 提示: Spring Boot 包含可插入的 API,允许支持各种不同的位置地址。 默认情况下,您可以导入 Java 属性、YAML 和“配置树”。第三方 jar 可以提供对其他技术的支持(不要求文件是本地的)。 例如,您可以想象配置数据来自外部存储,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。如果想支持自己的位置,请参阅 org.springframework.boot.context.config 包中的 ConfigDataLocationResolver 和 ConfigDataLoader 类。 #### 5. 导入无扩展名的文件 某些云平台无法为卷挂载文件添加文件扩展名。 要导入这些无扩展名的文件,需要给 Spring Boot 一个提示,以便它知道如何加载它们。 可以通过将扩展提示放在方括号中来做到这一点。 例如,假设有一个 /etc/config/myconfig 文件希望作为 yaml 导入。 可以使用以下命令从 application.properties 导入它: ``` spring: config: import: "file:/etc/config/myconfig[.yaml]" ``` #### 6. 使用配置树 在云平台上(比如Kubernetes) 直接配置会不安全。 许多云平台允许从数据卷中读取配置, 比如Kubernetes 可以批量挂载 ConfigMaps 和Secrets。 有两种常见的卷安装模式: 1. 单个文件包含一组完整的属性(通常编写为 YAML)。 2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”。 第一种方式可以使用spring.config.import.导入其他文件,对于第二种情况,使用 configtree: 前缀,以便 Spring Boot 知道它需要将所有文件公开为属性。 举例来说, 文件结构: ``` etc/ config/ myapp/ username password ``` 文件username存储用户名, password是加密的内容。 要导入这些属性,可以将以下内容添加到 application.properties 或 application.yaml 文件中:导入如下: ``` spring: config: import: "optional:configtree:/etc/config/" ``` 然后,您可以以通常的方式从环境中访问或注入 myapp.username 和 myapp.password 属性。 注意: 带有点符号的文件名也被正确映射。 例如,在上面的示例中,/etc/config 中名为 myapp.username 的文件将导致环境中的 myapp.username 属性。 提示: 配置树值可以绑定到字符串 String 和 byte[] 类型。 如果有多个配置树要从同一个父文件夹导入,可以使用通配符快捷方式。 任何以 /*/ 结尾的 configtree: location 将导入所有直接子项作为配置树。 例如: ``` etc/ config/ dbconfig/ db/ username password mqconfig/ mq/ username password ``` 可以使用 configtree:/etc/config/*/ 作为导入位置: ``` spring: config: import: "optional:configtree:/etc/config/*/" ``` 注意: 使用通配符加载的目录按字母顺序排序。 如果您需要不同的顺序,那么您应该将每个位置作为单独的导入列出 配置树也可用于 Docker secrets。 当 Docker swarm 服务被授予访问机密的权限时,机密会被挂载到容器中。 例如,如果名为 db.password 的secret挂在到位置 /run/secrets/,您可以使用以下命令使 db.password 可用于 Spring 环境: ``` spring: config: import: "optional:configtree:/run/secrets/" ``` #### 7. 属性占位符 application.properties 和 application.yml 中的值在使用时会根据环境进行过滤,因此可以参考以前定义的值(例如,来自系统属性)。 标准的 ${name} 属性占位符语法可以在值内的任何地方使用。 例如,以下文件会将 app.description 设置为“MyApp is a Spring Boot application”: ``` app: name: "MyApp" description: "${app.name} is a Spring Boot application" ``` 提示:可以使用此技术创建现有 Spring Boot 属性的“短”变体。 有关详细信息,请参阅“4. 使用“Short”(短)命令行参数”。 #### 8. 多文档文件的处理 Spring Boot 允许将单个物理文件拆分为多个独立添加的逻辑文档。 文档按顺序处理,从上到下。 后面的文档可以覆盖在前面的文档中定义的属性 对于 application.yml 文件,使用标准的 YAML 多文档语法。 三个连续的连字符代表一个文档的结束和下一个文档的开始。 例如下面的文件有两个逻辑文档: ``` spring: application: name: MyApp --- spring: application: name: MyCloudApp config: activate: on-cloud-platform: kubernetes ``` 对于 application.properties 文件,使用` #---` 注释用于标记文档拆分, 比如: ``` spring.application.name=MyApp #--- spring.application.name=MyCloudApp spring.config.activate.on-cloud-platform=kubernetes ``` ![](https://img.kancloud.cn/ba/34/ba34faf0cc82fdb5c99e4d51cea57b29_106x30.png)注意: 属性文件分隔符不能有任何前导空格,并且必须正好有三个连字符。 分隔符前后的行不能是注释。 ![](https://img.kancloud.cn/b1/2a/b12adb9ce1e7765f0ea5e264e56b8707_90x33.png)提示: 多文档属性文件通常与 spring.config.activate.on-profile 等激活属性结合使用。 ![](https://img.kancloud.cn/8c/8b/8c8b191b86c066271d62c24f554d68fc_143x30.png)警告:使用 @PropertySource 或 @TestPropertySource注解的多文档属性文件无法加载。 #### 9. 属性的激活 有时仅在满足某些条件时激活给定的属性 很有用。 例如,仅在特定配置文件处于活动状态时才相关的属性。 可以使用 spring.config.activate.* 有条件地激活属性文档。 激活属性可以使用: | 属性 | 说明 | | --- | --- | | on-profile | 与文档匹配的Profile表达式会被激活| | on-cloud-platform | 要使文档处于活动状态,必须检测到的 CloudPlatform。 | 例如,以下指定第二个文档仅在 Kubernetes 上运行时才处于活动状态,并且仅当“prod”或“staging”配置文件处于活动状态时: ``` myprop: always-set --- spring: config: activate: on-cloud-platform: "kubernetes" on-profile: "prod | staging" myotherprop: sometimes-set ``` ## 4. 加密属性 Spring Boot 不提供任何对加密属性值的内置支持,但是,它提供了修改 Spring Environment 中包含的值所需的钩子。 EnvironmentPostProcessor 接口允许您在应用程序启动之前操作环境。 详细参考[https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.application.customize-the-environment-or-application-context。](https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.application.customize-the-environment-or-application-context。) 如果寻找一种安全的方式来存储凭证和密码,Spring Cloud Vault 项目提供了在 HashiCorp Vault 中存储外部化配置的支持。 ## 5. YAML的使用 YAML 是 JSON 的超集,是一种用于指定分层配置数据的便捷格式。 只要在类路径上有 SnakeYAML 库,SpringApplication 类就会自动支持 YAML 作为属性的替代方案 注意: 如果使用启动器,则 SnakeYAML 由 spring-boot-starter 自动提供。 #### 1. 将 YAML 映射到属性 YAML 文档需要从其分层格式转换为可与 Spring Environment 一起使用的平面结构。 例如,考虑以下 YAML 文档: ``` environments: dev: url: https://dev.example.com name: Developer Setup prod: url: https://another.example.com name: My Cool App ``` 为了从 Environment 访问这些属性,它们将被展平如下: ``` environments.dev.url=https://dev.example.com environments.dev.name=Developer Setup environments.prod.url=https://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 ``` 提示:使用 [index] 表示法的属性可以使用 Spring Boot 的 Binder 类绑定到 Java List 或 Set 对象。 有关更多详细信息,请参阅下面的“类型安全配置属性”部分。 警告: 无法使用 @PropertySource 或 @TestPropertySource 批注加载 YAML 文件。 因此,如果需要以这种方式加载值,则需要使用属性文件。 #### 2. 直接加载 YAML Spring Framework 提供了两个方便的类,可用于加载 YAML 文档。 YamlPropertiesFactoryBean 将 YAML 作为属性加载,而 YamlMapFactoryBean 将 YAML 作为 Map 加载。 如果要将 YAML 作为 Spring PropertySource 加载,也可以使用 YamlPropertySourceLoader 类 ## 6. 配置随机值 RandomValuePropertySource 可用于注入随机值(例如,注入机密或测试用例)。 它可以生成整数、长整数、uuid 或字符串,如以下示例所示: ``` my: secret: "${random.value}" number: "${random.int}" bignumber: "${random.long}" uuid: "${random.uuid}" number-less-than-ten: "${random.int(10)}" number-in-range: "${random.int[1024,65536]}" ``` random.int* 语法是 OPEN value (,max) CLOSE,其中 OPEN,CLOSE 是任何字符,value,max 是整数。 如果提供了 max,则 value 是最小值,max 是最大值(不包括在内)。 ## 7. 配置系统环境变量 Spring Boot 支持为环境属性设置前缀。 如果系统环境由具有不同配置要求的多个 Spring Boot 应用程序共享,这将非常有用。 系统环境属性的前缀可以直接在 SpringApplication 上设置。 使用场景: 多个不同配置需求的Spring Boot 应用共享系统环境变量。 比如设置前缀为input,那么remote.timeout等属性在系统环境中也会解析为input.remote.timeout。