1. 外部化配置
Spring 支持外部化配置,比如application.properties 文件,application.yml,环境变量,命令行参数等。
1.1 配置随机数
1.1.1 application.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]}
random.int 语法如下所示:
比如:random.int [1024,65536]
1024是最小值,65536最大值
再比如: random.int(10) 表示小于10的整数
1.1.2 Controller 中这样调用
HomeController.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigInteger;
@RestController
public class HomeController {
@Value(value = "${my.secret}")
private String mySecret;
@Value(value = "${my.number}")
private Integer myNumber;
@Value(value = "${my.bignumber}")
private BigInteger myBigNumber;
@Value(value = "${my.uuid}")
private String uuid;
@Value(value ="${my.number.less.than.ten}")
private String myNumberLessThanTen;
@Value(value = "${my.number.in.range}")
private String myNumberInRange;
@GetMapping(value = "/test.do")
public String test(){
StringBuilder sb=new StringBuilder();
sb.append("my.secret:"+mySecret);
sb.append("--------------------");
sb.append("my.number:"+myNumber);
sb.append("--------------------");
sb.append("myBigNumber:"+mySecret);
sb.append("--------------------");
sb.append("my.uuid:"+uuid);
sb.append("--------------------");
sb.append("my.number.less.than.ten:"+myNumberLessThanTen);
sb.append("--------------------");
sb.append("my.number.in.range:"+myNumberInRange);
sb.append("--------------------");
String result=sb.toString();
return result;
}
}
访问效果:
1.2 访问命令行属性
比如application.properties 文件中有如下配置
server.port=8080
我们在运行的时候可以动态修改这个端口属性,通过–参数的方法。
比如这里我们可以使用–server.port 来覆盖application.properties 文件中的配置。
执行命令如下:
java -jar .\spring-boot-2-x-externalized-configuration-sample-0.0.1-SNAPSHOT.jar
--server.port=8081
执行成功后,端口就成功修改成了8081端口,而不是默认的8080端口
因为命令行属性始终优先于其他属性源
如果想要禁用这个命令行参数运行,设置这一行代码即可。
springApplication.setAddCommandLineProperties(false);
完整配置范例如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoot2XExternalizedConfigurationSampleApplication {
public static void main(String[] args) {
//SpringApplication.run(SpringBoot2XExternalizedConfigurationSampleApplication.class, args);
SpringApplication springApplication=new SpringApplication(SpringBoot2XExternalizedConfigurationSampleApplication.class);
springApplication.setAddCommandLineProperties(false);
springApplication.run(args);
}
}
1.3 Application Property 文件
SpringApplication 加载application.properties 文件,从下面这几个位置中查找
- ./config
- ./
- classpath:/config/
- classpath:/
按照这个列表中位置的优先级,优先级高的会覆盖优先级低的配置文件。
也可以通过命令行参数来动态加载单个配置文件
java -jar myproject.jar --spring.config.name=myproject
如果要加载两个配置文件就这么做:
java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
如果spring.config.location
包含目录(而不是文件),它们应该以 /
结束
默认情况下,配置文件的查找顺序是这样:
- file:./config/
- file:./
- classpath:/config/
- classpath:/
当指定了spring.config.location 之后,查找顺序就改变了。
- file:./custom-config/
- classpath:custom-config/classpath:custom-config/
当然我们也可以即保留默认值,然后额外地添加配置文件路径
classpath:/custom-config/,file:./custom-config/
这两个额外的配置添加后,查找顺序就变成了这样:
- file:./custom-config/
- classpath:custom-config/
- file:./config/
- file:./
- classpath:/config/
- classpath:/
1.4 Profile-specific Properties
由于开发,测试,生产环境有些地方需要不同的配置,因此Spring Boot 也提供了一种解决方案。
application-{profile}.properties
一般我们经常这么设置
- application.properties ---------写公共配置
- application-dev.properties-------写开发环境配置
- application-test.properties------写测试环境配置
- application-prod.properties-----写生产环境配置
假设我们想使用开发环境的配置就在application.properties 文件中指定这个属性
spring.profiles.active=dev
程序运行就会加载application.properties 和application-dev.properties 两个配置文件中的配置。
如果想切换到测试环境配置就修改为:
spring.profiles.active=test
程序运行就会加载application.properties 和application-test.properties 两个配置
1.5 Properties 文件中的占位符
application.properties 文件配置如下:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
Controller 中调用如下
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigInteger;
@RestController
public class HomeController {
@GetMapping(value = "/")
public String home(){
return "Hello World Spring Boot 2.x";
}
@Value(value = "${app.name}")
private String appName;
@Value(value = "${app.description}")
private String appDescription;
@GetMapping(value = "/test.do")
public String test(){
StringBuilder sb=new StringBuilder();
sb.append("appName:"+appName);
sb.append("-------------------");
sb.append("appDescription:"+appDescription);
sb.append("-------------------");
String result=sb.toString();
return result;
}
}
访问结果
1.6 加密属性值
Spring Boot没有为加密属性值提供任何内置支持
如果您正在寻找一种存储凭据和密码的安全方法, Spring Cloud Vault项目支持在HashiCorp Vault中存储外部化配置 。
1.7 使用yaml 文件替换properties 文件
YAML是JSON的超集,因此是用于指定分层配置数据的便捷格式。该SpringApplication级自动支持YAML来替代,只要你有属性 SnakeYAML在classpath库。
如果您使用“Starters”,则会自动提供SnakeYAML spring-boot-starter
1.7.1 YAML 文件的加载详解
Spring Framework 提供了两个类来加载YAMl 文件
YamlPropertiesFactoryBean将YMAL 文件转换成Properties 文件
YamlMapFactoryBean 再将Properties中的值 保存到Map里面
比如application.yaml 文件配置如下
environments:
dev:
url: http://dev.example.com
name: Developer Setup
prod:
url: http://another.example.com
name: My Cool App
它会首先转换成
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
比如这个配置文件
my:
servers:
- dev.example.com
- another.example.com
它会解析成这样
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
然后还用了一个配置类
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
注意:一旦使用@ConfigurationProperties自定义属性值注解,则需要添加下面这个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
1.7.2 YAML 文件也支持@Value注解
yaml 文件也支持@value 注解来访问yaml 文件中的配置属性值。
它是再YamlPropertySourceLoader这个类的帮助下实现的。
1.7.3 多个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
如果是development 文件被激活,那么server.address就使用127.0.0.1,如果
production & eu-central 文件被激活,那么server.address就使用192.168.1.120.如果都没有激活,则server.address使用默认的192.168.100
1.7.4 Properties 属性值类型安全
我们可以再Properties 文件里面配置自定义属性,但是似乎存在一定安全隐患,比如application.properties 文件中配置如下
acm.enabled=true
这个true 是Boolean 类型还是字符串类型呢?显然并没有做限制,这样是不太好的。
为了对这个属性值的数据类型做一定的约束,我们需要这么做:
AcmeProperties.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("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) { ... }
}
}
注意:一旦使用@ConfigurationProperties自定义属性值注解,则需要添加下面这个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
其次还需要在有Configuration注解的类中添加这个属性类配置
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
1.7.5 第三方的配置
@ConfigurationProperties 和Bean 组合使用来配置第三方库也是非常有帮助的。
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
1.7.6 @ConfigurationProperties 验证
Properties 文件中的属性值,有时候我们想添加一些验证,那么如何做呢?
首先需要类级别上添加@Validated 注解,其次在属性值上使用@NotNull 注解,@Valid@NotEmpty注解等
@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
}
}
1.7.7 自定义属性值最佳实践
虽然Spring Boot 仍然支持@Value注解访问application.properties 文件中自定义的属性值,但是该方法已经过时,不推荐使用。
最佳实践如下:
- applicaton.properties配置如下:
com.xingyun.config.username=xingyun
com.xingyun.config.age=27
com.xiingyun.config.sex=boy
- 创建一个叫做CustomProperties.Java 文件
@ConfigurationProperties(prefix="com.xingyun.config")
public class CustomProperties{
private String username;
private Integer age;
private String sex;
//getter and setter ...
}
- 在一个带有@Configuration 注解的类上添加@EnableConfigurationProperties(CustomProperties.class)注解配置
@EnableConfigurationProperties(BrdPicProperties.class)
@Configuration
public class MyConfig {
}
- 在需要使用的地方,使用@Autowired 注解CustomProperties 配置如下:
public class MyService{
@Autowired
CustomProperties customProperties;
pullic void testMethod(){
System.out.println(customProperties.getUserName());
System.out.println(customProperties.getAge());
System.out.println(customProperties.getSex());
}
}
本篇完~