爱生活,爱编码,微信搜一搜【架构技术专栏】关注这个喜欢分享的地方。本文 架构技术专栏 已收录,有各类JVM、多线程、源码视频、资料以及技术文章等你来拿java
1、概述
目前Spring Boot版本: 2.3.4.RELEASE,这更新的速度也是嗖嗖的了,随着新版本的发布,也一步步针对公司基础组件进行了升级改造,其中很重要的一块就是配置文件的更新(虽然目前已经所有使用了Apollo)。针对Spring Boot 新版本的配置文件也作了一次梳理,确实发现了之前没有注意到的点。mysql
2、新版的外部配置
一、基础配置加载
Spring Boot 为咱们提供了不少的外部配置参数,咱们可使用 YAML 文件(固然你也可使用properties,但不建议)、环境变量和命令行参数,来区分不一样的环境配置。redis
使用配置有两种方式:spring
- 使用注解@Value,来注入Environment 里面包含的属性
- 使用@ConfigurationProperties 来定义一个属性类,来包含咱们须要的属性(这些属性均可以配置在YAML中)
Spring Boot 外部配置这么多,那若是都配置了哪一个会生效呢?sql
Spring Boot会如下面的顺序来加载配置,优先级从高到低(相同配置优先级高的会覆盖低的),从外到里的来进行配置覆盖加载:json
1)开发者全局配置的properties文件(当开发者工具激活时,文件在$HOME/.config/spring-boot下的spring-boot-devtools.properties)微信
2)测试中配置了@TestPropertySource("base.properties") 注解来加载的配置,好比base.properties这种多线程
3)测试中 使用了@SpringBootTest的 properties架构
4)命令行参数配置,也就是java -jar后面使用的配置app
5)可使用SPRING_APPLICATION_JSON 属性加载的SON配置,加载方式有两种:
- 在系统环境变量加载 SPRING_APPLICATION_JSON='{"persion":{"name":"xxx"}}',这种加载会将这个数据加到Spring Environment中,咱们能够得到一个persion.name 的属性,值为xxx
- 使用System属性加载 java -Dspring.application.json='{"persion":{"name":"xxx"}}' -jar app.jar,这种加载方式会将spring.application.json属性的值当作一个String来加载,不会解析。
6)ServletConfig 初始化的配置
7)ServletContext初始化的配置
8)java:comp/env的JNDI特性
9)Java的系统属性,就是System.getProperties() 获取到的这些
10)操做系统配置的环境变量
11)在RandomValuePropertySource中配置的以random. 开头的属性
12)应用外部配置的 application-{profile}.properties或YAML ,可使用spring.profiles.active 来选择配置的环境,不选择默认就是application-default.properties。咱们可使用spring.config.location 来定义文件的路径进行加载。
13)在你应用内部配置的application-{profile}.properties 或 YAML,也是用于多环境加载选择使用,能够用spring.profiles.active 来激活配置
14)应用外部配置的application.properties或 YAML
15)应用内部配置的application-{profile}.properties 或 YAML。
这里1四、和15 的 SpringApplication 会从application.properties来进行配置属性的加载。
这个配置会从四个位置按照优先级从高到低的方式覆盖加载,高优先级覆盖低优先级的,来看下:
- 应用外部当前目录里 /config 文件夹下的 application.properties 或者application.yml
- 应用外部当前目录里的 application.properties 或者application.yml
- 应用内部classpath下的/config ,也就是resources/config 目录下的 application.properties 或者application.yml
- 应用内部classpath下,也就是resources 目录下的application.properties 或者application.yml
16)@Configuration 配置类上配置了 @PropertySource注解的,但在spring 上下文刷新前这个配置是不会被加载到Environment里面的。这种加载方式不能配置那些应用上下文刷新前就须要加载的属性,好比logging.* 和spring.main.* 这种。
使用方式:
//这里加载classpath:/com/myco/app.properties文件 @Configuration @PropertySource("classpath:/com/myco/app.properties") public class AppConfig { @Autowired Environment env; @Bean public TestBean testBean() { TestBean testBean = new TestBean(); testBean.setName(env.getProperty("testbean.name")); return testBean; } }
17)SpringApplication.setDefaultProperties 设置的参数
下面来用一个Demo 说下:
import org.springframework.stereotype.*; import org.springframework.beans.factory.annotation.*; @Component public class MyBean { @Value("${name}") private String name; // ... }
- 你可使用 classpath 下的application.yml来配置name= laowang
- 可使用一个外部的application.yml 来设置一个name = laoli 覆盖上一个配置 (当前name 获取的话是laoli)
- 在可使用java -jar app.jar --name="Spring" 在来覆盖上一个配置 (当前name获取的话是 Spring)
Spring Boot 配置文件也支持通配符的方式来加载,好比使用 spring.config.additional-location和spring.config.location来加载配置的时候就可使用通配符加载多个文件。
二、配置随机属性
随机属性的注入实际上是经过RandomValuePropertySource 来实现的,它能够帮咱们来产生integers、 longs、 uuid、strings 的随机值。这个对于咱们平时进行一些测试案例仍是很实用的。
//能够配置在application.yml 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]}
三、命令行属性
咱们可使用 java -jar --server.port=9000的方式将命令行参数server.port 添加到咱们应用的Environment,可使用@Value等方式获取。正常状况下命令行添加的属性优先级是我们优先级高的。
若是你不想将命令行的属性添加到应用的Environment中,那你能够配置SpringApplication.setAddCommandLineProperties(false)就好了。
其实使用命令行加载最多的可能就是--javaagent 这个属性了,对于如今的公司,APM监控那是必不可少的。咱们也能够经过命令参数的配置来临时的加载一些属性进行测试使用。
四、应用加载配置文件
其实上面已经说过了,这里在从新提一下。SpringApplication 会从application.yml里面加载属性配置,并将他们添加到Spring 的Environment中供咱们使用。优先级以下,高优先级覆盖低的(这里放个原版,能够本身尝试理解下):
- A
/config
subdirectory of the current directory - The current directory
- A classpath
/config
package - The classpath root
若是你不喜欢配置文件叫作application.properties,也可使用spring.config.name来进行配置。也可使用spring.config.location 来指定配置加载路径。
举例说明:
//修改个人配置文件叫myproject 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 配置是用来肯定应用须要加载哪些属性的,因此须要尽量早的加载。通常都是使用系统环境变量、系统参数、命令行加载的方式进行使用。
默认的配置加载路径以下,安装优先级从高到低排序(file:./config/ 优先级最高),因此在使用加载的时候必定要注意:
file:./config/
file:./config/*/
file:./
classpath:/config/
classpath:/
五、占位符的使用
在application.properties 咱们可使用占位符来进行属性的动态加载
好比咱们能够借助maven 的profile 在打包的时候动态的对环境参数进行替换(好比替换mysql 、redis等域名)
上几个例子:
//简单的使用 app.name=MyApp app.description=${app.name} is a Spring Boot application //配合命令行参数使用,如参数增长 --port=9000 来代替--server.port=9000,那在配置文件中咱们就能够配置 server.port=${port:8080}
注意一点:
若是你的POM 里面集成了spring-boot-starter-parent ,那么默认的maven-resources-plugins插件会使用@maven.token@来代替${maven.token}。这么作实际上是为了防止和Spring的占位符产生冲突,因此若是咱们使用maven 的profile 或者其余的来动态替换application.properties 内部的属性,请使用 @name@.
六、YAML文件进行多环境配置
1) 配置文件使用
在application.yml中,你可使用spring.profiles 来激活你想加载的环境配置内容。
例子:
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 那server.address 就是127.0.0.1。若是production & eu-central 被激活,那server.address 就是192.168.1.120。若是这三个我都没激活,那server.address 就是192.168.1.100,环境配置直接使用---来隔离开。
注意:spring.profiles 这个属性能够是一个名字,也能够是一个表达式。
2)@Profile注解使用
咱们不少时候会遇到组件动态选择的问题,好比我有多种的权限接入方式或者数据源选择性激活。但我又不想来来回回写点if else,那么@Profile就是一个神器了,他的到来使咱们的代码更加的灵活多变。
好比咱们重写一个属于源配置:
//第一个 @Configuration @Profile("development") public class StandaloneDataConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build(); } } //第二个 @Configuration @Profile("production") public class JndiDataConfig { @Bean(destroyMethod="") public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }
这样,咱们就能够根据不一样的配置来激活不一样的逻辑了,若是再能搭配上远程配置中心,那就更美丽了。
七、YAML的问题
1) YAML有不少优势,那必然也是有一丢丢的小毛病的。那就是YAML文件默认不能使用@PropertySource注解来进行配置加载。若是你不想进行多余的改造,那就老实的建一个properties文件用吧。
2)在YAML中配置多环境配置信息有的时候会有奇奇怪怪的问题,好比下面的:
application-dev.yml
server: port: 8000 --- spring: profiles: "!test" security: user: password: "secret"
若是此时我启动应用的时候加载了--spring.profiles.active=dev ,那我正常是应该获得security.user.password = secret,但真实的状况却不是这样。
由于咱们在配置文件名上使用了xxx-dev.yml,这时候当应用加载的时候就会直接找到application-dev.yml文件.而这时咱们配置文件内的多环境配置就失效了。
因此再多环境配置使用的时候,咱们要否则就选择xxx-dev.yml、xxx-pro.yml 这种方式,要否则就选择在一个文件内配置多环境。两者只能选一个,以避免出现恶心人的问题。
八、对象属性绑定
有时候咱们会有一组相同类型的属性须要加载,若是使用@Value 那真是累死人。这里Spring Boot为咱们提供了一个便捷的方式,咱们可使用一个类对所须要的变量进行统一的配置加载。
举个例子:
//属性类 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("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<String> roles = new ArrayList<>(Collections.singleton("USER")); public String getUsername() { ... } public void setUsername(String username) { ... } public String getPassword() { ... } public void setPassword(String password) { ... } public List<String> getRoles() { ... } public void setRoles(List<String> roles) { ... } } }
这时我在application.yml中配置以下属性,Spring Boot就会帮助咱们直接将属性绑定到AcmeProperties类中
acme.enabled =false acme.remote-address=127.0.0.1 acme.security.username=xxx
由于 @EnableConfigurationProperties 只是帮助咱们进行声明,在实际使用上咱们须要配合**@Configuration**,好比下面的配置,这样配置完后咱们就可使用@Resource 进行属性注入了。
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(AcmeProperties.class) public class MyConfiguration { }
九、宽松的绑定规则
上面聊了@ConfigurationProperties 能够帮助咱们进行对象属性绑定,其实在Spring Boot中为咱们提供了至关宽松的绑定规则。
好比context-path绑定到 contextPath属性,PORT绑定到 port属性,下面继续搞个Demo。
@ConfigurationProperties(prefix="acme.my-project.person") public class OwnerProperties { private String firstName; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } }
以上面的代码为例,咱们能在application.yml中下面的四种配置形式均可以将属性绑定到 firstName参数上,厉不厉害。
acme.my-project.person.first-name
acme.myProject.person.firstName
acme.my_project.person.first_name
ACME_MYPROJECT_PERSON_FIRSTNAME
固然,为了统一不出问题,建议都使用小写进行属性声明如 acme.my-project.person.first-name 。
十、属性绑定校验
在@ConfigurationProperties 声明的属性类上,咱们能够增长**@Validated** 来对配置属性进行校验。
@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 } }
爱生活,爱编码,微信搜一搜【架构技术专栏】关注这个喜欢分享的地方。本文 架构技术专栏 已收录,有各类JVM、多线程、源码视频、资料以及技术文章等你来拿