@ConfigurationProperties详解,以及该注解修饰的实体bean被注入后所有属性为null问题解决

15 篇文章 0 订阅

近期遇到了很奇怪的问题就是之前别人写好的代码,@ConfigurationProperties修饰的实体类被其他类注入之后可以正常使用,到我接手的时候就出现了奇怪的问题,wdnmd o(╥﹏╥)o 我们先看下这个类的使用方式,然后再讲问题的前因后果和处理方式。

ConfigurationProperties使用详解

在编写项目代码时,我们要求更灵活的配置,更好的模块化整合。在 Spring Boot 项目中,为满足以上要求,我们将大量的参数配置在 application.properties 或 application.yml 文件中,通过 @ConfigurationProperties 注解,我们可以方便的获取这些参数值。

先说下该注解的内部:

package org.springframework.boot.context.properties;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
    @AliasFor("prefix")
    String value() default "";

    @AliasFor("value")
    String prefix() default "";   //配置前缀,绑定配置文件中的配置该注解可以放在类上,也可以放在方法上

    boolean ignoreInvalidFields() default false; // 忽略不可用的属性

    boolean ignoreNestedProperties() default false; // 忽略嵌套属性

    boolean ignoreUnknownFields() default true; //

    /** @deprecated */
    @Deprecated
    boolean exceptionIfInvalid() default true;  //该属性是,如果有不可用的属性会抛出异常
}

prefix
该属性是用来指定属性前缀,只需要指定一个前缀,就能绑定有这个前缀的所有属性值。
例如:

course:
 num : 1
 name : 语文
@Data
@Component
@ConfigurationProperties(prefix = "course")
public class Course {
	private String name;
	private int num;
}

@ConfigurationProperties 的基本用法非常简单:我们为每个要捕获的外部属性提供一个带有字段的类。请注意以下几点:

前缀定义了哪些外部属性将绑定到类的字段上
根据 Spring Boot 宽松的绑定规则,类的属性名称必须与外部属性的名称匹配
我们可以简单地用一个值初始化一个字段来定义一个默认值
类本身可以是包私有的
类的字段必须有公共 setter 方法

ignoreInvalidFields
如果我们在 application.yml 属性上定义的属性不能被正确的解析会发生什么?假如我们为原本应该为布尔值的属性提供的值为 ‘tru’:

course:
  num : 1
  name : 语文
  isOK : tru

默认情况下,Spring Boot 将会启动失败,并抛出异常:

Failed to bind properties under 'myapp.mail.enabled' to java.lang.Boolean:

    Property: myapp.mail.enabled
    Value: foo
    Origin: class path resource [application.properties]:1:20
    Reason: failed to convert java.lang.String to java.lang.Boolean

当我们为属性配置错误的值时,而又不希望 Spring Boot 应用启动失败,我们可以设置 ignoreInvalidFields 属性为 true (默认为 false)

@Data
@Component
@ConfigurationProperties(prefix = "course",ignoreInvalidFields = true)
public class Course {
	private String name;
	private int num;
	private boolean isOK;
}

ignoreUnknownFields
和上面的情况有些相反,如果我们在 application.yml 文件提供了 Course 类不知道的属性会发生什么?

course:
  num : 1
  name : 语文
  iscxk : true

默认情况下,Spring Boot 会忽略那些不能绑定到 @ConfigurationProperties类字段的属性

然而,当配置文件中有一个属性实际上没有绑定到 @ConfigurationProperties类时,我们可能希望启动失败。也许我们以前使用过这个配置属性,但是它已经被删除了,这种情况我们希望被触发告知手动从 application.yml删除这个属性

为了实现上述情况,我们仅需要将 ignoreUnknownFields属性设置为 false (默认是 true)

现在,应用启动时,控制台会反馈我们异常信息

Binding to target [Bindable@cf65451 type = com.example.configurationproperties.Course, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(value=course, prefix=course, ignoreInvalidFields=false, ignoreUnknownFields=false)]] failed:

    Property: course.iscxk
    Value: true
    Origin: class path resource [application.properties]:3:29
    Reason: The elements [course.iscxk] were left unbound.

弃用警告⚠️(Deprecation Warning)
ignoreUnknownFields 在未来 Spring Boot 的版本中会被标记为 deprecated,因为我们可能有两个带有 @ConfigurationProperties 的类,同时绑定到了同一个命名空间 (namespace) 上,其中一个类可能知道某个属性,另一个类却不知道某个属性,这样就会导致启动失败。

启动时校验 @ConfigurationProperties
如果我们希望配置参数在传入到应用中时有效的,我们可以通过在字段上添加 bean validation 注解,同时在类上添加 @Validated 注解

@Data
@Component
@ConfigurationProperties(prefix = "course",ignoreInvalidFields = true)
public class Course {
	private String name;
	private int num;
	private boolean isOK;
}

如果我们忘记在 application.yml 文件设置 一个enabled 属性,并且设置对应值为空

应用启动时,我们将会得到 BindValidationException

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under ‘course’ to com.example.configurationproperties.Course failed:

Property: course.enabled
Value: null
Reason: must not be null

当然这些默认的验证注解不能满足你的验证要求,我们也可以自定义注解

如果你的验证逻辑很特殊,我们可以实现一个方法,并用 @PostConstruct 标记,如果验证失败,方法抛出异常即可, 关于 @PostConstruct,可以查看 Spring Bean 的生命周期,我从哪里来?

复杂属性类型
多数情况,我们传递给应用的参数是基本的字符串或数字。但是,有时我们需要传递诸如 List 的数据类型

List 和 Set
假如,我们班级添加几个学生的名字,我们可以添加该属性到 Course 类中

我们有两种方式让 Spring Boot 自动填充该 list 属性

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class Course {
	private String name;
	private int num;
	private List<String> students;
}

application.properties
在 application.properties 文件中以数组形式书写

course.students[0]=cxk1
course.students[1]=cxk2

application.yml
YAML 本身支持 list 类型,所以可以在 application.yml 文件中添加:

course:
  num : 1
  name : 语文
  students : 
    - cxk1
    - cxk2

当然我们还可以把学生摘出来,单独成一个类:

@Data
public class Student {
	private String name;
	private int age;
} 

然后把Course改造下,让课程里面有几个学生。

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class Course {
	private String name;
	private int num;
	private List<Student> students;
}

application.properties
在 application.properties 文件中以数组形式书写

course.students[0].name=cxk1
course.students[1].name=cxk2
course.students[0].age=22
course.students[1].age=21

application.yml
YAML 本身支持 list 类型,所以可以在 application.yml 文件中添加:

course:
  num : 1
  name : 语文
  students : 
    - name : cxk1
      age : 22
    - name : cxk2
      age : 21

Duration
Spring Boot 内置支持从配置参数中解析 durations (持续时间),官网文档 给出了明确的说明

我们既可以配置毫秒数数值,也可配置带有单位的文本:

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class DurationTest {
	private Duration pauseTimes;
}
course.pause-times=5s

官网上已明确说明,配置 duration 不写单位,默认按照毫秒来指定,我们也可已通过 @DurationUnit 来指定单位:
我们既可以配置毫秒数数值,也可配置带有单位的文本:

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class DurationTest {
	@DurationUnit (ChronoUnit.SECONDS)
	private Duration pauseTimes;
}

常用单位如下:

nsfor nanoseconds (纳秒)
us for microseconds (微秒)
ms for milliseconds (毫秒)
s for seconds (秒)
m for minutes (分)
h for hours (时)
d for days (天)

DataSize
与 Duration 的用法一毛一样,默认单位是 byte (字节),可以通过 @DataSizeUnit 单位指定:

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class DurationTest {
	@DataSizeUnit(DataUnit.MEGABYTES)
	private DataSize maxAttachmentSize = DataSize.ofMegabytes(2);
}

添加配置

course.max-attachment-size=2MB

但是,我测试的时候打印出来结果都是以 B (bytes) 来显示

常见单位如下:

B for bytes
KB for kilobytes
MB for megabytes
GB for gigabytes
TB for terabytes

该注解修饰的实体bean被注入后所有属性为null问题解决

然后我来讲述一下我在项目中遇到的有Setter Getter方法的实体bean 被注入后get不到属性的问题。
首先我代码是这样的,因为是公司的代码,所以我就不贴出来了,我大概还原下代码哈。

@Data
@Component
@ConfigurationProperties(prefix = "course")
public class CourseConfig {
	private String courseName;
	private Integer courseNum;
	private String COURSE_DESC_DETAIL;
}

然后配置文件呢,配置都没有任何问题,因为之前是可以使用的,后续到我手里就不能用了,get不到COURSE_DESC_DETAIL这个属性。并且DEBUG的时候发现,下面清一色的null。我焦头烂额、抓破头皮都没想到到底是哪里出了问题,然后我在每个set方法语句里面打印了一句话,输出set进去的值,发现每个都set进去值了,但是为什么get不到呢?当时我没有重写toString方法。然后我就把toString方法重写了。发现打印是正常的。这就奇怪了,然后继续往下找,我把每个属性都打印了一遍,发现就唯独这个COURSE_DESC_DETAIL打印不出来。是个null,然后我就思来想去也不知道是什么原因,最后在网上发现一篇帖子,说可能是由于框架版本引起的,带有_的实体属性不能get到。然后我就抱着试试的心态发现雀食是酱紫,然后芜湖~~~(这个驼峰不是我写的 (#.#) )

  总结:
  		前缀定义了哪些外部属性将绑定到类的字段上
		根据 Spring Boot 宽松的绑定规则,类的属性名称必须与外部属性的名称匹配
		我们可以简单地用一个值初始化一个字段来定义一个默认值
		类本身可以是包私有的
		类的字段必须有公共 setter 方法
		我再在这里补充一条:请使用标准驼峰命名法定义属性。请使用标准驼峰命名法定义属性。请使用标准驼峰命名法定义属性。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@NestedConfigurationProperty注解是Spring框架提供的一种注解,用于在@ConfigurationProperties注解中嵌套配置属性类。它的作用是将一个类中的属性作为另一个类的属性进行配置。 使用@NestedConfigurationProperty注解时,需要注意以下几点: 1. 该注解应该用于配置属性类中的嵌套属性。 2. 被注解的嵌套属性类需要提供getter和setter方法。 3. 嵌套属性类上还可以使用其他的@ConfigurationProperties注解,以便进行更复杂的配置。 与@ConfigurationProperties注解相比,@NestedConfigurationProperty注解主要用于解决嵌套属性类在使用@ConfigurationProperties注解时的问题@ConfigurationProperties注解用于将外部配置文件中的属性值绑定到一个JavaBean对象上,而@NestedConfigurationProperty注解则用于在该JavaBean对象中嵌套其他的配置属性类。 @ConfigurationProperties注解可以将外部配置文件中的多个属性值绑定到一个JavaBean对象上,而@NestedConfigurationProperty注解则用于将一个JavaBean对象中的多个属性作为另一个JavaBean对象的属性进行配置。这样可以更好地组织和管理配置属性。 总结来说,@NestedConfigurationProperty注解用于在@ConfigurationProperties注解中嵌套配置属性类,而@ConfigurationProperties注解用于将外部配置文件中的属性值绑定到JavaBean对象上。它们可以一起使用,以便更好地组织和管理配置属性

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值