首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【基础系列】ConfigurationProperties 配置绑定中那些你不知道的事情

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

原创
作者头像
一灰灰blog
修改于 2021-01-18 06:27:14
修改于 2021-01-18 06:27:14
4.2K0
举报
文章被收录于专栏:小灰灰小灰灰

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

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

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

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

<!-- more -->

I. 项目环境

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

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

代码语言:txt
AI代码解释
复制
<!-- 这个依赖是干嘛的,后文会介绍 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
</dependencies>

II. ConfigurationProperties 详解

1. 配置绑定

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

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

代码语言:txt
AI代码解释
复制
@Data
@ConfigurationProperties(prefix = "hhui.bind")
public class BindConfig {
    private String name;

    private Integer age;

    private List<String> list;

    private Map<String, String> map;
}

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

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

对应的配置文件如下

代码语言:txt
AI代码解释
复制
hhui:
  bind:
    name: YiHui
    age: 18
    list:
      - java
      - c
      - python
    map:
      wechat: 小灰灰blog
      blogs: http://blog.hhui.top
      git: http://github.com/liuyueyi

注意事项

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

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

代码语言:txt
AI代码解释
复制
@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;
    }
}

2. 注册生效

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

a. @Component等注解修饰方式

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

代码语言:txt
AI代码解释
复制
@Data
@Component
@ConfigurationProperties(prefix = "hhui.bind")
public class BindConfig {
}

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

b. @Bean注册

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

代码语言:txt
AI代码解释
复制
@Configuration
public class AutoConfiguration {
    @Bean
    public BindConfig bindConfig() {
        return new BindConfig();
    }
}
c. @EnableConfigurationProperties方式

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

代码语言:txt
AI代码解释
复制
@EnableConfigurationProperties({BindConfig.class})
@Configuration
public class AutoConfiguration {
}
d. 小结

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

3. 参数类型不匹配

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

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

代码语言:txt
AI代码解释
复制
hhui:
  bind:
    Name: YiHui
    AGE: 18y
    list:
      - java
      - c
      - python
    map:
      wechat: 小灰灰blog
      blogs: http://blog.hhui.top
      git: http://github.com/liuyueyi

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

代码语言:txt
AI代码解释
复制
@SpringBootApplication
public class Application {

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

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

}

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

代码语言:txt
AI代码解释
复制
@Data
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true)
public class BindConfig {
}

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

代码语言:txt
AI代码解释
复制
BindConfig(name=YiHui, age=null, list=[java, c, python], map={wechat=小灰灰blog, blogs=http://blog.hhui.top, git=http://github.com/liuyueyi})

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

说明

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

  • 直接在配置类中,设置属性的默认值,表示当这个配置不存在或者设置非法时,使用默认的配置
代码语言:txt
AI代码解释
复制
@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;
}

再次执行输出如

代码语言:txt
AI代码解释
复制
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))

4. 配置解析规则

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

a. POJO,List,Map 参数类型

我们新定义一个 Pwd 类

代码语言:txt
AI代码解释
复制
@Data
public class Pwd {
    private String user;

    private String pwd;

    private Integer code;
}

然后扩展一下BindConfig

代码语言:txt
AI代码解释
复制
@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;
}

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

代码语言:txt
AI代码解释
复制
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

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

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

b.自定义配置解析

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

代码语言:txt
AI代码解释
复制
hhui:
  bind:
    Jwt: '{"token": "11111111123", "timestamp": 1610880489123}'

对应的 Jwt 类如下

代码语言:txt
AI代码解释
复制
@Data
public class Jwt {
    private String token;

    private Long timestamp;
}

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

代码语言:txt
AI代码解释
复制
@Component
@ConfigurationPropertiesBinding
public class JwtConverter implements Converter<String, Jwt> {
    @Override
    public Jwt convert(String source) {
        return JSONObject.parseObject(source, Jwt.class);
    }
}

说明

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

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

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

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

5. 配置不存在场景

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

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

代码语言:txt
AI代码解释
复制
hhui:
  bind:
    notExist: true

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

6. 参数校验

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

添加 pom 依赖

代码语言:txt
AI代码解释
复制
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

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

代码语言:txt
AI代码解释
复制
@Data
@Validated
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true, ignoreUnknownFields = false)
public class BindConfig {
    @Min(13)
    @Max(66)
    private Integer age = 18;
}

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

代码语言:txt
AI代码解释
复制
hhui:
  bind:
    age: 10

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

7. IDEA 自动补全提示

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

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

添加文章最开头的依赖

代码语言:txt
AI代码解释
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

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

代码语言:txt
AI代码解释
复制
{
  "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": []
}

然后自动补全就有了

说明

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. 项目

项目源码

系列博文

1. 一灰灰 Blog

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

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
HarmonyOS应用开发者高级认证(88分答案)
D. 取消订阅公共事件 2端云一体化工程目录结构分为哪些部分 A. 端开发工程(Application)
红目香薰
2023/10/11
22.8K0
HarmonyOS应用开发者高级认证(88分答案)
HarmonyOS应用开发——页面布局技巧
我们将对于多页面以及更多有趣的功能展开叙述,这次我们对于 HarmonyOS 的很多有趣常用组件并引出一些其他概念以及解决方案、页面跳转传值、生命周期、启动模式(UiAbility),样式的书写、状态管理以及动画等方面进行探讨
小帅聊鸿蒙
2024/06/25
4870
HarmonyOS应用开发——页面布局技巧
常见鸿蒙应用开发面试题
.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:16px;overflow-x:hidden;color:#252933}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:24px;line-height:38px;margin-bottom:5px}.markdown-body h2{font-size:22px;line-height:34px;padding-bottom:12px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:20px;line-height:28px}.markdown-body h4{font-size:18px;line-height:26px}.markdown-body h5{font-size:17px;line-height:24px}.markdown-body h6{font-size:16px;line-height:24px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.
万少
2025/03/27
2710
常见鸿蒙应用开发面试题
HarmonyOS NEXT实战:启动页
##HarmonyOS Next实战##HarmonyOS SDK应用服务##教育##
中雨
2025/06/25
610
鸿蒙开发实战:揭秘页面与项目生命周期,实现精准监控
在鸿蒙应用开发中,每一个页面和组件都承载着特定的生命周期。这些生命周期阶段,如同生命的轨迹,记录着页面从诞生到消亡的每一个重要时刻。深入理解和监控这些生命周期,不仅能提升应用性能,还能帮助我们更好地把握用户体验。
用户2475552
2025/06/29
1400
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 三)
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
爱吃土豆丝的打工人
2023/10/15
7820
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 三)
【HarmonyOS 5】鸿蒙页面和组件生命周期函数
在 ArkUI 中,页面组件指的是被@Entry装饰的组件,其拥有独特的生命周期接口,这些接口对页面在不同状态下的行为控制起着关键作用。
GeorgeGcs
2025/05/08
2660
HarmonyOS NEXT-Flutter混合开发之鸿蒙-代码实践
在 Flutter 三端分离模式下完成纯血鸿蒙混入的过程中,虽然官方文档提供了一定的指导,但实际操作中可能会遇到一些坑。以下是我在适配过程中的一些经验总结,供各位开发者参考 😄 如果有帮助点个赞。
用户8181473
2025/03/17
4110
软x、中x、城x、翼x、拓x、华xxOD2月鸿蒙面试核心汇总
录音可以通过AVRecorder和AudioCapturer来实现。两者区别主要在支持录制声音的格式不同和控制录音文件的细小粒度不同上。AVRecorder会简单一些,AudioCapturer会复杂一些-还可以搭配ai语音功能使用
万少
2025/02/18
2060
软x、中x、城x、翼x、拓x、华xxOD2月鸿蒙面试核心汇总
Flutter鸿蒙终端一体化-天下一统
但更多的时候,我们需要的是一种类似FlutterFragment的方式来进行引用,可喜的是,鸿蒙实现这种方式也并不复杂,因为不论是FlutterPage,还是FlutterFragment,它内部实际上是通过FlutterView的方式来创建的,所以,很快就有开发者提了PR,让鸿蒙可以支持FlutterFragment的方式进行开发,这个组件就是——FlutterEntry,原始PR地址如下。 https://gitee.com/openharmony-sig/flutter_engine/pulls/116
用户1907613
2024/03/18
4440
Flutter鸿蒙终端一体化-天下一统
HarmonyOS-ArkTS-UIAbility生命周期——【坚果派-红目香薰】
浏览、切换和退出应用时,应用中UlAbility实例会在其生命周期的不同状态之间转换。
红目香薰
2024/01/25
5520
HarmonyOS-ArkTS-UIAbility生命周期——【坚果派-红目香薰】
鸿蒙自定义组件生命周期
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
龙儿筝
2024/11/19
2380
鸿蒙自定义组件生命周期
01. HarmonyOS应用开发实践与技术解析
随着华为HarmonyOS生态的不断发展,越来越多的开发者开始关注并投入到HarmonyOS应用开发中。本文将通过一个实际的项目案例,详细讲解HarmonyOS应用开发的核心技术和最佳实践,帮助开发者快速掌握HarmonyOS应用开发的要点。
全栈若城
2025/03/03
2180
01. HarmonyOS应用开发实践与技术解析
【前端转鸿蒙必看篇】:组件生命周期
不管是 react 还是 vue,我们写组件写页面的时候,通常都会使用生命周期,会在一些特别的生命周期函数里,只写一个必要的逻辑,比如 componentDidMount 执行一些 sdk 的初始化,或者数据的初始化请求,componentWillUnMount 里进行一些定时器的清楚。在 ArkUI 里也有类似的生命周期;
小帅聊鸿蒙
2024/10/30
3450
【前端转鸿蒙必看篇】:组件生命周期
HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转
先看下通过应用程序框架UIAbility、启动模式和路由参数跳转,我们实现的简单Demo效果图:
小帅聊鸿蒙
2024/06/18
9700
HarmonyOS应用开发——程序框架UIAbility、启动模式与路由跳转
HarmonyOS Next 自定义组件@Component的生命周期
用户4773577
2025/06/27
720
【鸿蒙基于API 13实战开发】—— ArkUI 组件:Router切换Navigation
从ArkUI组件树层级上来看,原先由Router管理的page在页面栈管理节点stage的下面。Navigation作为导航容器组件,可以挂载在单个page节点下,也可以叠加、嵌套。Navigation管理了标题栏、内容区和工具栏,内容区用于显示用户自定义页面的内容,并支持页面的路由能力。Navigation的这种设计上有如下优势:
小帅聊鸿蒙
2025/01/17
2240
【鸿蒙基于API 13实战开发】—— ArkUI 组件:Router切换Navigation
华为HarmonyOS鸿蒙应用开发初体验
HarmonyOS鸿蒙系统出来好几年了,开发鸿蒙应用和咱平时的前端开发到底有什么差别呢,下面就从一个前端开发的视角带领大家体验一下HarmonyOS开发。
人人都是码农
2023/11/15
1K0
华为HarmonyOS鸿蒙应用开发初体验
遥遥领先!HarmonyOS ArkTS页面和自定义组件生命周期
上一篇文章我们介绍了 《遥遥领先! HarmonyOS 自定义组件的结构、函数、变量、参数规定,这篇太干了我要渴死了!!!》 现在我们系统的看看 ArkTS页面和自定义组件生命周期 的实现流程步骤
杨不易呀
2023/12/05
1K0
遥遥领先!HarmonyOS ArkTS页面和自定义组件生命周期
HarmonyOS NEXT 实战系列09-生命周期
aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行
用户8181473
2025/03/17
1320
推荐阅读
相关推荐
HarmonyOS应用开发者高级认证(88分答案)
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档