一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • 基础系列

    • 配置

      • 【基础系列】SpringBoot基础篇配置信息之如何读取配置信息
      • 【基础系列】SpringBoot基础篇配置信息之多环境配置信息
      • 【基础系列】SpringBoot基础篇配置信息之自定义配置指定与配置内引用
      • 【基础系列】SpringBoot配置信息之配置刷新
      • 【基础系列】SpringBoot配置信息之默认配置
      • 【基础系列】实现一个自定义配置加载器(应用篇)
      • 【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示
      • 【基础系列】ConfigurationProperties配置绑定中那些你不知道的事情
        • I. 项目环境
        • II. ConfigurationProperties详解
          • 1. 配置绑定
          • 2. 注册生效
          • 3. 参数类型不匹配
          • 4. 配置解析规则
          • 5. 配置不存在场景
          • 6. 参数校验
          • 7. IDEA自动补全提示
          • 8.小结
        • II. 其他
          • 0. 项目
          • 1. 一灰灰Blog
      • 【基础系列】SpringBoot基础篇@Value中哪些你不知道的知识点
      • 【基础系列】SpringBoot之自定义配置源的使用姿势
      • 【基础系列】SpringBoot @Value之字面量及SpEL知识点介绍篇
      • 【基础系列】SpringBoot应用篇@Value注解支持配置自动刷新能力扩展
      • 【基础系列】基于maven多环境配置
    • AOP

    • Bean

    • SpEL

    • 事件

    • 国际化

    • 定时器

    • 日志

  • DB系列

  • 搜索系列

  • MQ系列

  • WEB系列

  • 中间件

  • 运维

  • SpringSecurity

  • SpringCloud

  • Spring系列
  • 基础系列
  • 配置
一灰灰
2021-01-17

【基础系列】ConfigurationProperties配置绑定中那些你不知道的事情

在SpringBoot项目中,获取配置属性可以说是一个非常简单的事情,将配置写在aplication.yml文件之后,我们就可以直接通过@Value注解来绑定并获取;此外我们也可以将一个结构化的配置,借助@ConfigurationPorperties绑定到一个POJO,然后供项目使用,那么在使用它的时候,不知是否有想过

  • @ConfigurationPorperties修饰的类如何生效
  • 配置参数与定义的POJO类型不匹配时会怎样
  • 配置参数的必要性校验可以怎么支持
  • 自定义的配置参数,idea中如何自动补全
  • 已废弃的参数定义,怎样友好的提示使用方
  • List/Map格式的参数,怎么使用
  • 自定义参数解析规则如何支持

如果上面这些都已经了然于心,那么本文的帮助将不会特别大;如果对此有所疑问,接下来将逐一进行解惑

# I. 项目环境

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

下面是核心的pom.xml(源码可以再文末获取)

<!-- 这个依赖是干嘛的,后文会介绍 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
</dependencies>
1
2
3
4
5
6
7

# II. ConfigurationProperties详解

# 1. 配置绑定

假定我们现在自定义一个功能模块,里面有一些我们自定义的参数,支持通过yaml配置文件的方式注入

首先我们可以先定义一个配置类 BindConfig

@Data
@ConfigurationProperties(prefix = "hhui.bind")
public class BindConfig {
    private String name;

    private Integer age;

    private List<String> list;

    private Map<String, String> map;
}
1
2
3
4
5
6
7
8
9
10
11

请注意上面的注解中,prefix = hhui.bind,简单来讲就是会读取配置文件中,前缀为 hhui.bind 的属性,然后依次赋值到这个类中

  • BindConfig.name = hhui.bind.name
  • BindConfig.age = hhui.bind.age
  • ...

对应的配置文件如下

hhui:
  bind:
    name: YiHui
    age: 18
    list:
      - java
      - c
      - python
    map:
      wechat: 小灰灰blog
      blogs: http://blog.hhui.top
      git: http://github.com/liuyueyi
1
2
3
4
5
6
7
8
9
10
11
12

注意事项

  • 配置类必须有公共的Setter方法,上文中主要是借助lombok的@Data省略了Setter方法的显示声明而已
  • 类的属性名与配置文件中的配置名要求匹配
    • 大小写不敏感
    • 支持下划线转驼峰
  • 配置类不要求必须是public

关于上面最后一点,也就表明我们可以在自动AutoConfiguration类中,声明一个内部类来绑定配置信息,如下

@Configuration
@EnableConfigurationProperties({AutoConfiguration.BindConfig.class})
public class AutoConfiguration {

    @Data
    @ConfigurationProperties(prefix = "hhui.bind")
    static class BindConfig {

        private String name;

        private Integer age;

        private List<String> list;

        private Map<String, String> map;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2. 注册生效

我们通过@ConfigurationProperties修饰配置类之后,是否直接会生效呢?通常来讲,让它生效有下面三种方式

# a. @Component等注解修饰方式

直接在配置类上添加@Component, @Configuration等注解,让Spring容器扫描并加载它

@Data
@Component
@ConfigurationProperties(prefix = "hhui.bind")
public class BindConfig {
}
1
2
3
4
5

使用这种方式时,需要注意配置类在自动扫描的包路径下,否则可能不会被扫描(主要是作为第三方jar包提供服务时,可能出现扫描不到的问题)

# b. @Bean注册

把它当成一个普通的bean,借助bean注册的方式来实现,也是一个可选的方案,一般的实现方式如下

@Configuration
public class AutoConfiguration {
    @Bean
    public BindConfig bindConfig() {
        return new BindConfig();
    }
}
1
2
3
4
5
6
7

# c. @EnableConfigurationProperties方式

在配置类上,添加这个注解之后,可以实现配置注册,一般常见的使用姿势如

@EnableConfigurationProperties({BindConfig.class})
@Configuration
public class AutoConfiguration {
}
1
2
3
4

# d. 小结

上面三种注册方式,前面两种的思路是将配置类作为bean,第三种实现思路和主动注册bean一致(所以想实现主动注册bean,可以考虑它的实现逻辑)

# 3. 参数类型不匹配

如果我们在配置中,一个本来希望接收int类型的参数,结果实际上填了一个非整形,会怎样?

比如前面的配置类,我们实际的配置文件将age填18y,来看一下最终会发生什么事情

hhui:
  bind:
    Name: YiHui
    AGE: 18y
    list:
      - java
      - c
      - python
    map:
      wechat: 小灰灰blog
      blogs: http://blog.hhui.top
      git: http://github.com/liuyueyi
1
2
3
4
5
6
7
8
9
10
11
12

简单演示,直接在启动类中测试一下会如何

@SpringBootApplication
public class Application {

    public Application(BindConfig config) {
        System.out.println(config);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12

参数异常之后,直接启动失败,如果对参数的要求没有那么严格,即允许失败,我们可以通过设置ignoreInvalidFields = true

@Data
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true)
public class BindConfig {
}
1
2
3
4

再次执行之后,会发现正常启动,输出如下

BindConfig(name=YiHui, age=null, list=[java, c, python], map={wechat=小灰灰blog, blogs=http://blog.hhui.top, git=http://github.com/liuyueyi})
1

注意查看上面的age,因为传入的参数非法,所以是null

说明

结合默认值 + ignoreInvalidFields 方式来支持配置的最大可用性:

  • 直接在配置类中,设置属性的默认值,表示当这个配置不存在或者设置非法时,使用默认的配置
@Data
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true)
public class BindConfig {

    private String name;

    private Integer age = 18;

    private List<String> list;

    private Map<String, String> map;
}
1
2
3
4
5
6
7
8
9
10
11
12

再次执行输出如

BindConfig(name=YiHui, age=18, list=[java, c, python], map={wechat=小灰灰blog, blogs=http://blog.hhui.top, git=http://github.com/liuyueyi}, mainPwd=Pwd(user=一灰灰blog, pwd=yihuihui, code=9))
1

# 4. 配置解析规则

常见的配置除了基本类型之外,能嵌套自定义对象么,非基本类型又可以如何解析呢?

# a. POJO,List,Map参数类型

我们新定义一个Pwd类

@Data
public class Pwd {
    private String user;

    private String pwd;

    private Integer code;
}
1
2
3
4
5
6
7
8

然后扩展一下BindConfig

@Data
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true)
public class BindConfig {

    private String name;

    private Integer age = 18;

    private List<String> list;

    private Map<String, String> map;

    private Pwd mainPwd;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个时候mainPwd对应的yaml配置文件可以如下设置

hhui:
  bind:
    Name: YiHui
    AGE: 1h
    list:
      - java
      - c
      - python
    map:
      wechat: 小灰灰blog
      blogs: http://blog.hhui.top
      git: http://github.com/liuyueyi
    # 下面这个对应的是 BindConfg.mainPwd; 可以写成 main_pwd也可以写成mainPwd
    main_pwd:
      user: 一灰灰blog
      pwd: yihuihui
      code: 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

从上面的介绍也可以看出,对于自定义的POJO类是支持的,使用姿势也没什么区别

此外,对于List和Map的使用也给出了实例

# b.自定义配置解析

上面我们自定义的Pwd类,主要借助setter方法,将匹配的属性塞入进去;如果我的配置就是一个json串,可以注入到一个POJO类么

hhui:
  bind:
    Jwt: '{"token": "11111111123", "timestamp": 1610880489123}'
1
2
3

对应的Jwt类如下

@Data
public class Jwt {
    private String token;

    private Long timestamp;
}
1
2
3
4
5
6

这个时候如想实现上面的配置解析,可以通过实现org.springframework.core.convert.converter.Converter接口来支持,并通过@ConfigurationPropertiesBinding注解来表明这是个配置属性转换类,不加这个注解会不生效哦

@Component
@ConfigurationPropertiesBinding
public class JwtConverter implements Converter<String, Jwt> {
    @Override
    public Jwt convert(String source) {
        return JSONObject.parseObject(source, Jwt.class);
    }
}
1
2
3
4
5
6
7
8

说明

使用自定义的配置解析规则时,注意两点

  • 实现接口Converter
  • 使用@ConfigurationPropertiesBinding修饰注解

Spring提供了一些默认的配置解析规则,如

  • 文件大小DataSize
    • 对应的value可以是 1B, 1KB, 1MB, 1GB...
  • 持续时间Duration
    • 对应的value可已是 1ns,1us,1ms,1s,1m,1h,1d

# 5. 配置不存在场景

一个配置类,对应的类中没有这个属性会怎样?

如针对前面的BindConfig,没有notExist这个属性,但是配置文件中,却加上了这个

hhui:
  bind:
    notExist: true
1
2
3

实测之后,发现没有任何影响,通过查看@ConfigurationProperties注解的成员,发现可以设置ignoreUnknownFields=false,从字面上表示出现了未能识别的成员,不会略错误,但是在实际测试中,并没有生效

# 6. 参数校验

参数校验可以说比较常用的case了,比如前面的配置age,基本上不会允许这个参数能是负数,如需要对参数进行校验,我们可以借助@Validated来实现校验

添加pom依赖

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
1
2
3
4

然后再配置类上添加@Validated,然后就可以在需要校验的字段上添加对应的限制

@Data
@Validated
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true, ignoreUnknownFields = false)
public class BindConfig {
    @Min(13)
    @Max(66)
    private Integer age = 18;
}
1
2
3
4
5
6
7
8

如果我们将age参数设置不满足上面的条件

hhui:
  bind:
    age: 10
1
2
3

再次测试会发现报如下错误

# 7. IDEA自动补全提示

平时在Spring开发过程中,在yaml文件中添加配置时,配合idea有非常友好的提示,可以非常友好的补全参数配置

那么我们自定义的参数想实现这个效果应该怎么做呢?

添加文章最开头的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
1
2
3
4

添加上面的依赖之后,打包mvn clean package,然后会发现在META-INF下面有个spring-configuration-metadata.json

{
  "groups": [
    {
      "name": "hhui.bind",
      "type": "com.git.hui.boot.bind.config.BindConfig",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
    }
  ],
  "properties": [
    {
      "name": "hhui.bind.age",
      "type": "java.lang.Integer",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig",
      "defaultValue": 18
    },
    {
      "name": "hhui.bind.jwt",
      "type": "com.git.hui.boot.bind.config.Jwt",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
    },
    {
      "name": "hhui.bind.list",
      "type": "java.util.List<java.lang.String>",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
    },
    {
      "name": "hhui.bind.main-pwd",
      "type": "com.git.hui.boot.bind.config.Pwd",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
   },
    {
      "name": "hhui.bind.map",
      "type": "java.util.Map<java.lang.String,java.lang.String>",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
    },
    {
      "name": "hhui.bind.name",
      "type": "java.lang.String",
      "sourceType": "com.git.hui.boot.bind.config.BindConfig"
    }
  ],
  "hints": []
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

然后自动补全就有了

说明

idea推荐添加插件Spring Assistant,支持非常友好的配置注入

# 8.小结

本文介绍了@ConfigurationProperties修饰POJO类,实现配置的绑定,可以通过将这个类声明为一个普通bean的方式进行注册,也可以借助@EnableConfigurationProperties来注册

在配置参数时,需要注意如果参数类型不一致,会导致项目启动失败;可以通过设置ConfigurationProperties#ignoreInvalidFields = true,来避免这种场景

通过实现接口Converter + @ConfigurationPropertiesBinding来自定义参数解析转换规则,可以实现各路姿势的参数解析

配置的自动提示支持也比较简单,添加org.springframework.boot:spring-boot-configuration-processor依赖,打包之后在META-INF中会多一个json文件spring-configuration-metadata.json

# II. 其他

# 0. 项目

项目源码

  • 工程:https://github.com/liuyueyi/spring-boot-demo (opens new window)
  • 源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-properties-bind (opens new window)

系列博文

  • 【基础系列】实现一个自定义配置加载器(应用篇) (opens new window)
  • 【基础系列】SpringBoot配置信息之默认配置 (opens new window)
  • 【基础系列】SpringBoot配置信息之配置刷新 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之自定义配置指定与配置内引用 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之多环境配置信息 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之如何读取配置信息 (opens new window)

# 1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰Blog个人博客 https://blog.hhui.top (opens new window)
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top (opens new window)

一灰灰blog

编辑 (opens new window)
#ConfigurationProperties#Config
上次更新: 2021/10/15, 19:56:22
【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示
【基础系列】SpringBoot基础篇@Value中哪些你不知道的知识点

← 【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示 【基础系列】SpringBoot基础篇@Value中哪些你不知道的知识点→

最近更新
01
【基础系列】基于maven多环境配置
04-25
02
【WEB系列】内嵌Tomcat配置Accesslog日志文件生成位置源码探索
04-24
03
【搜索系列】ES查询常用实例演示
04-18
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×