SpringBoot 允许你外部化你的配置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用 properties文件、YAML 文件、环境变量和命令行参数来外部化配置。可以使用 @Value 注解直接将属性值注入到 Bean里面,也可以通过 @ConfigurationProperties 将属性绑定到结构化对象中。
SpringBoot 会默认加载类路径下的 application.properties 配置,延续上一篇中的例子,我们在配置文件中添加上允许登录的用户名及密码。
application.properties 配置:
user.name = 13632672222
user.password = 123456
用户 Controller:
@RestController
@RequestMapping("/sys/user")
public class UserController {
@Value("${user.name}")
private String userName;
@Value("${user.password}")
private String password;
@RequestMapping("login")
public Result login(@RequestBody @Valid UserModel userModel) {
if (!userName.equals(userModel.getUsername())
|| !password.equals(userModel.getPassword())) {
return new Result(ResultCode.PASSWORD_ERROR);
}
User user = new User();
user.setUsername(userModel.getUsername());
user.setPassword(userModel.getPassword());
return new Result(ResultCode.SUCCESS, user);
}
}
系统用户一般不会只存在一个,如果需要配置多个用户,很显示上面的注入方式是无法满足需求的,那就需要使用到 @ConfigurationProperties 注解了。
application.properties 配置:
sys.users[0].username = 13632672222
sys.users[0].password = 123456
sys.users[0].age = 18
sys.users[1].username = 13800000001
sys.users[1].password = 654321
sys.users[1].age = 20
Properties 映射对象:
@ConfigurationProperties(prefix = "sys")
public class SystemProperties {
private List users = new ArrayList<>();
public List getUsers() {
return users;
}
public void setUsers(List users) {
this.users = users;
}
}
Mapping 方法:
@Autowired
private SystemProperties systemProperties;
@RequestMapping("list")
public Result list() {
return new Result(ResultCode.SUCCESS, systemProperties.getUsers());
}
调用用户查询接口,获取用户信息如下,可以看到用户的年龄也自动转成了所需要的 int 类型:
开发人员一般是无法管理生产环境的,应用的部署及维护是运维人员的工作,如果运维小伙伴一不小心漏掉了某个参数怎么办?虽然部署文档可以在一定程度上规避这些问题,但仍然存在一定的隐患,为了尽量减少此类问题,我们可以给外部配置加上相应的校验规则。
要求必须配置用户,且所有用户年龄都需要在20岁以上:
@ConfigurationProperties(prefix = "sys")
public class SystemProperties {
@NotEmpty
@Valid
private List users;
...
}
---
public class User {
private String username;
private String password;
@Min(20)
private int age;
...
}
如果上面的配置不做任何更新,那么就会得到启动异常:
Description:
Binding to target com.qchery.funda.props.SystemProperties@63192798 failed:
Property: sys.users[0].age
Value: 18
Reason: 最小不能小于20
Action:
Update your application's configuration
YAML 是 JSON 的超集,它是一种非常方便的数据格式,用于指定带有层次结构的配置数据。当你的类路径里面带有 snakeyaml 库的时候,SpringApplication 类会自动支持 YAML 来替代 properties 配置。使用 application.yml 替换 application.properties 如下:
sys:
users:
- username: 13632672222
password: 123456
age: 22
- username: 13800000001
password: 654321
age: 20
在 User 类里面添加一个新的字段 nickName,并在配置文件中添加相应的配置:
sys:
users:
- username: 13632672222
password: 123456
age: 22
nickName: 张三
- username: 13800000001
password: 654321
age: 20
nick-name: 赵四
当我们再次调用查询用户接口时,会返回如下报文:
{
"code": 0,
"msg": "请求成功",
"data": [
{
"username": "13632672222",
"age": 22,
"nickName": "张三"
},
{
"username": "13800000001",
"age": 20,
"nickName": "赵四"
}
]
}
如果仔细观察配置文件的话,会发现两个用户配置 nickName 的方式是不一致的,一个是 nickName,一个是 nick-name,但它们两个都可以成功,这是因为 SpringBoot 将环境属性绑定到 @ConfigurationProperties 定义 Bean 时,使用了一些比较宽松的规则,主要包括下面几种变形:
属性 | 标注 |
---|---|
users.nickName | 标准的驼峰语法 |
users.nick-name | 虚线符号,推荐在 properties 及 yml 文件中使用 |
users.nick_name | 下划线符号,properties 及 yml 里面的一种替代格式 |
USERS_NICK_NAME | 大写格式化,推荐在环境变量中使用 |
开发的过程中,可能存在很多套环境,开发、集成、测试等,每个环境的配置都不一样,想要更好地维护这些环境,需要将相关的配置文件隔离开来,SpringBoot 里面提供了两种方式来帮我们完成这项任务。
对每一套环境创建一个对应的配置文件,如果开发使用 dev,集成使用 int,那么就可以创建两个配置文件,分别命名成 applicationj-dev.yml 与 application-int.yml,通过在 application.yml 里面声明需要激活的配置文件变更系统配置。
application-dev.yml 配置:
sys:
users:
- username: 13632672222
password: 123456
age: 22
nickName: 张三
application-int.yml 配置:
sys:
users:
- username: 13800000001
password: 654321
age: 20
nick-name: 赵四
application.yml 配置:
spring:
profiles:
active: int
最终效果如下:
application.yml 文件一般是存放在包内,那就存在着一个问题,生产配置无法由开发人员管理,项目打包也就不会将其打包到应用程序里面去。要解决这个问题,需要使用到 spring.config.location 这个配置项,由于这个配置项会被用来决定配置文件的加载路径,所以,它需要在环境属性中定义,如:系统变量、命令行参数等。
去除 application.yml 文件,直接指定配置文件路径,得到效果如下:
注意:当使用 spring.config.location 时,spring.profiles.active 配置项将会失效,也就是在这种模式下是不区分 profile 的,示例中只是为了让读者看的更加明白,所以使用的不同的文件名而已。
推荐一个比较好的加密工具(jasypt),下面我们来看一下该工具的用法:
首先,需要添加 Jar 包的依赖项配置:
"com.github.ulisesbocchio:jasypt-spring-boot-starter:1.12"
其次,配置解密需要用到的算法及密钥:
jasypt:
encryptor:
password: funda
algorithm: PBEWithMD5AndDES
然后,使用上面配置的算法及密钥对需要加密的数据进行加密,使用 jasypt-1.9.2.jar 对明文 root 进行加密:
java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="root" password=funda algorithm=PBEWithMD5AndDES
该命令需要在 jasypt-1.9.2.jar 所在的路径下执行,得到输出结果如下:
----ENVIRONMENT-----------------
Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.112-b16
----ARGUMENTS-------------------
algorithm: PBEWithMD5AndDES
input: root
password: funda
----OUTPUT----------------------
CJ0EN4egw8l1k6aCtr9JMA==
最后,将加密后的结果配置到配置文件中:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
username: ENC(CJ0EN4egw8l1k6aCtr9JMA==)
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
加密后的配置值需要使用 ENC 进行包括,这样 jasypt 才会识别出该值是一个密文,需要进行解密操作。
项目的 github 地址:https://github.com/qchery/funda