SpringBoot @ConfigurationProperties + Validation实现启动期校验解决方案

 更新时间:2025年09月15日 10:50:47   作者:小明知意  
本文给大家介绍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)下的哪个属性(urlsecret-key)出了问题,以及原因。运维同学无需再问开发,直接看日志就能完成配置。
  • 沟通成本降低:开发人员只需提供配置属性类的定义(可以作为文档),运维按图索骥即可。message 属性中的中文提示更是大大提升了沟通效率。
  • 代码更健壮:配置集中管理,结构清晰,避免了散落的 @Value,易于维护和扩展。

小明将改造后的代码提交上线后,部门老大对此表示了肯定。从此,团队的配置管理流程变得更加规范,再也没有发生过因配置缺失导致的线上故障。

到此这篇关于SpringBoot @ConfigurationProperties + Validation实现启动期校验解决方案的文章就介绍到这了,更多相关SpringBoot @ConfigurationProperties Validation启动校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java使用JDBC向MySQL数据库批次插入10W条数据(测试效率)

    Java使用JDBC向MySQL数据库批次插入10W条数据(测试效率)

    使用JDBC连接MySQL数据库进行数据插入的时候,特别是大批量数据连续插入(100000),如何提高效率呢?今天小编通过本教程给大家介绍下
    2016-12-12
  • Java list移除元素相关操作指南

    Java list移除元素相关操作指南

    这篇文章主要给大家介绍了关于Java list移除元素相关操作的相关资料,文中介绍的方法包括增强for循环、迭代器、Stream流和removeIf()方法,同时还介绍了如何从一个列表中删除包含另一个列表元素的方法,以及如何删除指定下标位置的元素,需要的朋友可以参考下
    2024-12-12
  • java多线程模拟交通灯管理系统

    java多线程模拟交通灯管理系统

    这篇文章主要为大家详细介绍了java多线程模拟交通灯管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • SpringBoot集成WebSocket实现后台向前端推送信息的示例

    SpringBoot集成WebSocket实现后台向前端推送信息的示例

    这篇文章主要介绍了SpringBoot集成WebSocket实现后台向前端推送信息的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • MyBatis如何通过xml方式实现SaveOrUpdate

    MyBatis如何通过xml方式实现SaveOrUpdate

    这篇文章主要讲如何通过xml方式实现SaveOrUpdate,但是仍然建议在Service中实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-06-06
  • visual studio 2019安装配置可编写c/c++语言的IDE环境

    visual studio 2019安装配置可编写c/c++语言的IDE环境

    这篇文章主要介绍了visual studio 2019安装配置可编写c/c++语言的IDE环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Android studio按钮点击页面跳转详细步骤

    Android studio按钮点击页面跳转详细步骤

    在Android应用程序中,页面跳转是非常常见的操作,下面这篇文章主要给大家介绍了关于Android studio按钮点击页面跳转的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Java图形界面开发之简易记事本

    Java图形界面开发之简易记事本

    这篇文章主要为大家详细介绍了Java图形界面开发之简易记事本的制作方法,,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Java用编码检测代理ip是否有效

    Java用编码检测代理ip是否有效

    这篇文章主要为大家详细介绍了Java如何使用编码检测代理ip是否有效,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-12-12
  • SpringBoot带你实现一个点餐小程序

    SpringBoot带你实现一个点餐小程序

    有个小伙伴临时找到我,要开发一个点餐的系统,时间比较着急,给了2天的时间。马马虎虎的搞出来了,头发掉了一撮!下面介绍下本系统,感兴趣的小伙伴,可以参考开发下
    2022-07-07

最新评论