--- title: SpringBoot 之属性加载详解 date: 2019-01-10 11:55:54 order: 32 categories: - Java - 框架 - Spring - Spring核心 tags: - Java - 框架 - Spring - SpringBoot permalink: /pages/7f4217a7/ --- # SpringBoot 之属性加载详解 ## 加载 property 顺序 Spring Boot 加载 property 顺序如下: 1. [Devtools 全局配置](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-devtools-globalsettings) (当 devtools 被激活 `~/.spring-boot-devtools.properties`). 2. [测试环境中的 `@TestPropertySource` 注解配置](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html) 3. 测试环境中的属性 `properties`:[`@SpringBootTest`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html) 和 [测试注解](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests). 4. 命令行参数 5. `SPRING_APPLICATION_JSON` 属性 6. `ServletConfig` 初始化参数 7. `ServletContext` 初始化参数 8. JNDI attributes from 通过 `java:comp/env` 配置的 JNDI 属性 9. Java 系统属性 (`System.getProperties()`) 10. 操作系统环境比那里 11. `RandomValuePropertySource` 加载 `random.*` 形式的属性 12. jar 包外的 `application-{profile}.properties` 或 `application-{profile}.yml` 配置 13. jar 包内的 `application-{profile}.properties` 或 `application-{profile}.yml` 配置 14. jar 包外的 `application.properties` 或 `application.yml` 配置 15. jar 包内的 `application.properties` 或 `application.yml` 配置 16. `@PropertySource` 绑定的配置 17. 默认属性 (通过 `SpringApplication.setDefaultProperties` 指定) ## 随机属性 `RandomValuePropertySource` 类用于配置随机值。 示例: ```properties 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]} ``` ## 命令行属性 默认情况下, `SpringApplication` 会获取 `--` 参数(例如 `--server.port=9000` ),并将这个 `property` 添加到 Spring 的 `Environment` 中。 如果不想加载命令行属性,可以通过 `SpringApplication.setAddCommandLineProperties(false)` 禁用。 ## Application 属性文件 `SpringApplication` 会自动加载以下路径下的 `application.properties` 配置文件,将其中的属性读到 Spring 的 `Environment` 中。 1. 当前目录的 `/config` 子目录 2. 当前目录 3. classpath 路径下的 `/config` package 4. classpath 根路径 > 注: > > 以上列表的配置文件会根据顺序,后序的配置会覆盖前序的配置。 > > 你可以选择 [YAML(yml)](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-yaml) 配置文件替换 properties 配置文件。 如果不喜欢 `application.properties` 作为配置文件名,可以使用 `spring.config.name` 环境变量替换: ``` $ java -jar myproject.jar --spring.config.name=myproject ``` 可以使用 `spring.config.location` 环境变量指定配置文件路径: ```properties $ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties ``` ## Profile 特定属性 如果定义 `application-{profile}.properties` 形式的配置文件,将被视为 `profile` 环境下的特定配置。 可以通过 `spring.profiles.active` 参数来激活 profile,如果没有激活的 profile,默认会加载 `application-default.properties` 中的配置。 ## 属性中的占位符 `application.properties` 中的值会被 `Environment` 过滤,所以,可以引用之前定义的属性。 ``` app.name=MyApp app.description=${app.name} is a Spring Boot application ``` > 注:你可以使用此技术来创建 Spring Boot 属性变量。请参考: [Section 77.4, “Use ‘Short’ Command Line Arguments](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-use-short-command-line-arguments) ## YAML 属性 Spring 框架有两个类支持加载 YAML 文件。 - `YamlPropertiesFactoryBean` 将 YAML 文件的配置加载为 `Properties` 。 - `YamlMapFactoryBean` 将 YAML 文件的配置加载为 `Map` 。 示例 1 ```yaml environments: dev: url: http://dev.example.com name: Developer Setup prod: url: http://another.example.com name: My Cool App ``` 等价于: ```properties 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 支持列表形式,等价于 property 中的 `[index]` : ```yaml my: servers: - dev.example.com - another.example.com ``` 等价于 ```properties my.servers[0]=dev.example.com my.servers[1]=another.example.com ``` ### 访问属性 `YamlPropertySourceLoader` 类会将 YAML 配置转化为 Spring `Environment` 类中的 `PropertySource` 。然后,你可以如同 properties 文件中的属性一样,使用 `@Value` 注解来访问 YAML 中配置的属性。 ### 多 profile 配置 ```yaml 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 ``` ### YAML 的缺点 注:YAML 注解中的属性不能通过 `@PropertySource` 注解来访问。所以,如果你的项目中使用了一些自定义属性文件,建议不要用 YAML。 ## 属性前缀 ```java 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(prefix="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 roles = new ArrayList<>(Collections.singleton("USER")); public String getUsername() { ... } public void setUsername(String username) { ... } public String getPassword() { ... } public void setPassword(String password) { ... } public List getRoles() { ... } public void setRoles(List roles) { ... } } } ``` 相当于支持配置以下属性: - `acme.enabled` - `acme.remote-address` - `acme.security.username` - `acme.security.password` - `acme.security.roles` 然后,你需要使用 `@EnableConfigurationProperties` 注解将属性类注入配置类中。 ```java @Configuration @EnableConfigurationProperties(AcmeProperties.class) public class MyConfiguration { } ``` ## 属性松散绑定规则 Spring Boot 属性名绑定比较松散。 以下属性 key 都是等价的: | Property | Note | | ----------------------------------- | -------- | | `acme.my-project.person.first-name` | `-` 分隔 | | `acme.myProject.person.firstName` | 驼峰命名 | | `acme.my_project.person.first_name` | `_` 分隔 | | `ACME_MYPROJECT_PERSON_FIRSTNAME` | 大写字母 | ## 属性转换 如果需要类型转换,你可以提供一个 `ConversionService` bean (一个名叫 `conversionService` 的 bean) 或自定义属性配置 (一个 `CustomEditorConfigurer` bean) 或自定义的 `Converters` (被 `@ConfigurationPropertiesBinding` 注解修饰的 bena)。 ### 时间单位转换 Spring 使用 `java.time.Duration` 类代表时间大小,以下场景适用: - 除非指定 `@DurationUnit` ,否则一个 long 代表的时间为毫秒。 - ISO-8601 标准格式( [`java.time.Duration`](https://docs.oracle.com/javase/8/docs/api//java/time/Duration.html#parse-java.lang.CharSequence-) 的实现就是参照此标准) - 你也可以使用以下支持的单位: - `ns` - 纳秒 - `us` - 微秒 - `ms` - 毫秒 - `s` - 秒 - `m` - 分 - `h` - 时 - `d` - 天 示例: ```java @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; } } ``` ### 数据大小转换 Spring 使用 `DataSize` 类代表数据大小,以下场景适用: - long 值(默认视为 byte) - 你也可以使用以下支持的单位: - `B` - `KB` - `MB` - `GB` - `TB` 示例: ```java @ConfigurationProperties("app.io") public class AppIoProperties { @DataSizeUnit(DataUnit.MEGABYTES) private DataSize bufferSize = DataSize.ofMegabytes(2); private DataSize sizeThreshold = DataSize.ofBytes(512); public DataSize getBufferSize() { return this.bufferSize; } public void setBufferSize(DataSize bufferSize) { this.bufferSize = bufferSize; } public DataSize getSizeThreshold() { return this.sizeThreshold; } public void setSizeThreshold(DataSize sizeThreshold) { this.sizeThreshold = sizeThreshold; } } ``` ## 校验属性 ```java @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。获取这个 `@Bean` 的方法必须声明为 `static`。 ## 示例源码 > 示例源码:[spring-boot-property](https://github.com/dunwu/spring-boot-tutorial/tree/master/codes/spring-boot-property) ## 参考资料 - [Spring Boot 官方文档之 boot-features-external-config](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config)