SpringBoot @ConfigurationProperties + Validation实现启动期校验解决方案
作者:小明知意
小明成长记:@ConfigurationProperties+Validation实现启动期校验
事故回顾:周三下午 2 点,监控系统突然告警,某核心服务的错误日志激增。小明和小伙伴们立刻被拉进紧急会议。经排查,发现错误原因是:一个新上线的短信推送功能,其服务商API URL在线上环境的配置文件中完全缺失。导致RestTemplate在调用时,注入的String类型的 URL 为null,最终在运行时抛出了NullPointerException。
甩锅现场:
- 部门老大:“这个功能测试环境不是好好的吗?为什么线上挂了?谁负责的配置?”
- 运维同事:“部署流程都是自动化的,配置是根据开发提供的清单来管理的。清单里没写这个新配置项,我们当然不知道要配。”
- 小明(内心OS):“完了,这个功能是我做的。我以为大家都会看代码,知道要配这个……”
老大批示:
“小明,这次的问题根源是配置管理不规范。你立刻整改代码,要让程序在启动的时候自己告诉我们缺了什么配置,而不是等到用户用的时候才崩溃! 以后绝不允许再发生这种低级错误!”
小明的任务:
对代码进行改造,利用 @ConfigurationProperties 在应用启动阶段对关键参数进行强校验,避免配置缺失导致运行时出错。
小明的改造方案:使用@ConfigurationProperties进行启动期校验
小明决定采用 Spring Boot 官方推荐的 @ConfigurationProperties + Validation 方案。这样,如果配置缺失或不符合规则,应用将根本无法启动,并在日志中明确打印出缺失的配置项,运维同学直接拿着错误信息去补配置即可。
第一步:引入校验依赖
首先,他检查了项目的 pom.xml,确保包含了 spring-boot-starter-validation 依赖。这是实现校验功能的基础。
<!-- 如果使用的是 Spring Boot 2.3+ 版本,需要显式引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>第二步:创建配置属性类并添加校验规则
小明为短信服务创建了一个专门的配置属性类,将所有相关的配置项集中管理。
package com.example.demo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* 短信服务配置
* 使用 @Validated 注解开启校验
* 使用 @ConfigurationProperties 绑定前缀为 "sms" 的配置
*/
@Component
@Validated // 关键注解1:告诉Spring需要对此Bean进行校验
@ConfigurationProperties(prefix = "sms") // 关键注解2:绑定配置前缀
public class SmsProperties {
/**
* 服务商API地址
* 本次事故的罪魁祸首!
* @NotBlank 确保该值不能为 null 或空字符串
*/
@NotBlank(message = "【短信服务】关键配置 'sms.url' 未配置,请检查配置文件!")
private String url;
/**
* 授权密钥
*/
@NotBlank(message = "【短信服务】关键配置 'sms.secret-id' 未配置")
private String secretId;
/**
* 操作密钥
*/
@NotBlank(message = "【短信服务】关键配置 'sms.secret-key' 未配置")
private String secretKey;
/**
* 重试次数
* @NotNull 确保数字类型的配置不为空
* @Min 设置最小值
*/
@NotNull(message = "【短信服务】配置 'sms.retry-times' 未配置")
@Min(value = 0, message = "【短信服务】配置 'sms.retry-times' 的值不能小于0")
private Integer retryTimes;
/**
* 是否启用短信服务(生产环境才启用,开发测试环境可能不启用)
* 这里使用了默认值 false,即使不配置也不会报错
*/
private Boolean enabled = false;
// 标准的 Getter 和 Setter 方法必须存在,否则配置无法注入
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getSecretId() {
return secretId;
}
public void setSecretId(String secretId) {
this.secretId = secretId;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public Integer getRetryTimes() {
return retryTimes;
}
public void setRetryTimes(Integer retryTimes) {
this.retryTimes = retryTimes;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}第三步:在业务代码中注入使用
改造原来的代码,不再直接使用 @Value 注入单个属性,而是注入这个完整的配置 Bean。
package com.example.demo.service;
import com.example.demo.config.SmsProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SmsService {
// 直接注入配置Bean
private final SmsProperties smsProperties;
@Autowired
public SmsService(SmsProperties smsProperties) {
this.smsProperties = smsProperties;
}
public void sendSms(String phoneNumber, String content) {
// 使用配置
if (Boolean.TRUE.equals(smsProperties.getEnabled())) {
String targetUrl = smsProperties.getUrl(); // 这里拿到的url肯定是有效的
String secretId = smsProperties.getSecretId();
// ... 实际调用发送逻辑
System.out.println("正在调用短信服务: " + targetUrl);
} else {
System.out.println("短信服务未启用,已忽略发送。");
}
}
}第四步:准备配置文件
错误的配置 (application.yml) - 用于复现事故:
sms: # url: https://api.sms-service.com/send # 故意注释掉,模拟遗忘配置 secret-id: your_secret_id_here # secret-key: your_secret_key_here # 也注释掉一个 retry-times: -1 # 配置了一个非法的值
第五步:启动应用,观察效果
当小明启动应用时,应用根本无法启动,控制台会立即打印出如下非常清晰的错误信息:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'sms' to com.example.demo.config.SmsProperties failed:
Property: sms.url
Value: null
Reason: 【短信服务】关键配置 'sms.url' 未配置,请检查配置文件!
Property: sms.secret-key
Value: null
Reason: 【短信服务】关键配置 'sms.secret-key' 未配置
Property: sms.retry-times
Value: -1
Reason: 【短信服务】配置 'sms.retry-times' 的值不能小于0
Action:
Update your application's configuration改造后的成效
- 启动即发现:配置问题在应用启动之初就被发现,无法形成部署包,根本不会影响线上用户。
- 责任清晰:错误信息极其明确,直接指出是哪个配置前缀(
sms)下的哪个属性(url、secret-key)出了问题,以及原因。运维同学无需再问开发,直接看日志就能完成配置。 - 沟通成本降低:开发人员只需提供配置属性类的定义(可以作为文档),运维按图索骥即可。
message属性中的中文提示更是大大提升了沟通效率。 - 代码更健壮:配置集中管理,结构清晰,避免了散落的
@Value,易于维护和扩展。
小明将改造后的代码提交上线后,部门老大对此表示了肯定。从此,团队的配置管理流程变得更加规范,再也没有发生过因配置缺失导致的线上故障。
到此这篇关于SpringBoot @ConfigurationProperties + Validation实现启动期校验解决方案的文章就介绍到这了,更多相关SpringBoot @ConfigurationProperties Validation启动校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- springboot使用@ConfigurationProperties实现自动绑定配置参数属性
- SpringBoot中的@ConfigurationProperties注解的使用
- SpringBoot中@ConfigurationProperties自动获取配置参数的流程步骤
- SpringBoot中@Value获取值和@ConfigurationProperties获取值用法及比较
- springboot中@ConfigurationProperties无效果的解决方法
- SpringBoot中@ConfigurationProperties注解的使用与源码详解
- SpringBoot利用validation实现数据校验完整指南
- SpringBoot使用spring-boot-starter-validation实现参数校验
- SpringBoot利用Validation包实现高效参数校验
