乐趣区

关于java:基础系列ConfigurationProperties-配置绑定中那些你不知道的事情

【根底系列】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(源码能够再文末获取)

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

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

请留神下面的注解中,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

注意事项

  • 配置类必须有公共的 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;
    }
}

2. 注册失效

咱们通过 @ConfigurationProperties 润饰配置类之后,是否间接会失效呢?通常来讲,让它失效有上面三种形式

a. @Component等注解润饰形式

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

@Data
@Component
@ConfigurationProperties(prefix = "hhui.bind")
public class BindConfig {}

应用这种形式时,须要留神配置类在主动扫描的包门路下,否则可能不会被扫描(次要是作为第三方 jar 包提供服务时,可能呈现扫描不到的问题)

b. @Bean注册

把它当成一个一般的 bean,借助 bean 注册的形式来实现,也是一个可选的计划,个别的实现形式如下

@Configuration
public class AutoConfiguration {
    @Bean
    public BindConfig bindConfig() {return new BindConfig();
    }
}

c. @EnableConfigurationProperties形式

在配置类上,增加这个注解之后,能够实现配置注册,个别常见的应用姿态如

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

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

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

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

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

}

参数异样之后,间接启动失败,如果对参数的要求没有那么严格,即容许失败,咱们能够通过设置ignoreInvalidFields = true

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

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

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 形式来反对配置的最大可用性:

  • 间接在配置类中,设置属性的默认值,示意当这个配置不存在或者设置非法时,应用默认的配置
@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;
}

再次执行输入如

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 类

@Data
public class Pwd {
    private String user;

    private String pwd;

    private Integer code;
}

而后扩大一下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;
}

这个时候 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

从下面的介绍也能够看出,对于自定义的 POJO 类是反对的,应用姿态也没什么区别

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

b. 自定义配置解析

下面咱们自定义的 Pwd 类,次要借助 setter 办法,将匹配的属性塞入进去;如果我的配置就是一个 json 串,能够注入到一个 POJO 类么

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

对应的 Jwt 类如下

@Data
public class Jwt {
    private String token;

    private Long timestamp;
}

这个时候如想实现下面的配置解析,能够通过实现 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);
    }
}

阐明

应用自定义的配置解析规定时,留神两点

  • 实现接口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

实测之后,发现没有任何影响,通过查看 @ConfigurationProperties 注解的成员,发现能够设置ignoreUnknownFields=false,从字面上示意呈现了未能辨认的成员,不会略谬误,然而在理论测试中,并没有失效

6. 参数校验

参数校验能够说比拟罕用的 case 了,比方后面的配置 age,基本上不会容许这个参数能是正数,如须要对参数进行校验,咱们能够借助@Validated 来实现校验

增加 pom 依赖

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

而后再配置类上增加@Validated,而后就能够在须要校验的字段上增加对应的限度

@Data
@Validated
@ConfigurationProperties(prefix = "hhui.bind", ignoreInvalidFields = true, ignoreUnknownFields = false)
public class BindConfig {@Min(13)
    @Max(66)
    private Integer age = 18;
}

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

hhui:
  bind:
    age: 10

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

7. IDEA 主动补全提醒

平时在 Spring 开发过程中,在 yaml 文件中增加配置时,配合 idea 有十分敌对的提醒,能够十分敌对的补全参数配置

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

增加文章最结尾的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

增加下面的依赖之后,打包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": []}

而后主动补全就有了

阐明

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
  • 源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-properties-bind

系列博文

  • 【根底系列】实现一个自定义配置加载器(利用篇)
  • 【根底系列】SpringBoot 配置信息之默认配置
  • 【根底系列】SpringBoot 配置信息之配置刷新
  • 【根底系列】SpringBoot 根底篇配置信息之自定义配置指定与配置内援用
  • 【根底系列】SpringBoot 根底篇配置信息之多环境配置信息
  • 【根底系列】SpringBoot 根底篇配置信息之如何读取配置信息

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因集体能力无限,不免有疏漏和谬误之处,如发现 bug 或者有更好的倡议,欢送批评指正,不吝感谢

上面一灰灰的集体博客,记录所有学习和工作中的博文,欢送大家前去逛逛

  • 一灰灰 Blog 集体博客 https://blog.hhui.top
  • 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top
  • 微信公众号: 一灰灰 blog

退出移动版