SpringBoot实战 之 外部配置篇

SpringBoot 允许你外部化你的配置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用 properties文件、YAML 文件、环境变量和命令行参数来外部化配置。可以使用 @Value 注解直接将属性值注入到 Bean里面,也可以通过 @ConfigurationProperties 将属性绑定到结构化对象中。

1. 配置注入

1.1. @Value 注入

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);
    }

}

测试效果如下,配置成功:
SpringBoot实战 之 外部配置篇_第1张图片

1.2. @ConfigurationProperties 注入

系统用户一般不会只存在一个,如果需要配置多个用户,很显示上面的注入方式是无法满足需求的,那就需要使用到 @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 类型:

SpringBoot实战 之 外部配置篇_第2张图片

1.3. 参数合法性校验

开发人员一般是无法管理生产环境的,应用的部署及维护是运维人员的工作,如果运维小伙伴一不小心漏掉了某个参数怎么办?虽然部署文档可以在一定程度上规避这些问题,但仍然存在一定的隐患,为了尽量减少此类问题,我们可以给外部配置加上相应的校验规则。

要求必须配置用户,且所有用户年龄都需要在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

2. YAML 配置

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 大写格式化,推荐在环境变量中使用

3. 配置分离

开发的过程中,可能存在很多套环境,开发、集成、测试等,每个环境的配置都不一样,想要更好地维护这些环境,需要将相关的配置文件隔离开来,SpringBoot 里面提供了两种方式来帮我们完成这项任务。

3.1. Profile 的使用

对每一套环境创建一个对应的配置文件,如果开发使用 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

最终效果如下:

3.2. 指定配置路径

application.yml 文件一般是存放在包内,那就存在着一个问题,生产配置无法由开发人员管理,项目打包也就不会将其打包到应用程序里面去。要解决这个问题,需要使用到 spring.config.location 这个配置项,由于这个配置项会被用来决定配置文件的加载路径,所以,它需要在环境属性中定义,如:系统变量、命令行参数等。

去除 application.yml 文件,直接指定配置文件路径,得到效果如下:

注意:当使用 spring.config.location 时,spring.profiles.active 配置项将会失效,也就是在这种模式下是不区分 profile 的,示例中只是为了让读者看的更加明白,所以使用的不同的文件名而已。

4. 配置加密

推荐一个比较好的加密工具(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

你可能感兴趣的:(springboot)