You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Spring使用@ConfigurationProperties占位符未解析时启动失败?

很棒的问题!@ConfigurationProperties默认会把无法解析的占位符保留为字面量,这在你需要严格校验必填环境变量时确实很棘手。下面我会分享两种可靠的解决方案,让Spring在启动时如果遇到无法解析的占位符直接失败,不用再做容易出错的字符串检查。

方案一:全局强制占位符解析

如果你希望应用中所有占位符(包括@Value和@ConfigurationProperties中的)都必须能被解析,可以覆盖Spring默认的PropertySourcesPlaceholderConfigurer,拒绝无法解析的占位符。

创建一个配置类来定义这个Bean:

@Configuration
public class StrictPlaceholderConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        // 禁用忽略无法解析的占位符
        configurer.setIgnoreUnresolvablePlaceholders(false);
        return configurer;
    }
}

工作原理:这个Bean会告诉Spring,只要有任何占位符(比如${MY_ENV_VAR})无法从环境变量、配置文件或其他属性源中解析,就立即抛出异常。应用会在完全初始化前启动失败,这正是我们想要的效果。

注意:这是全局设置——应用中所有无法解析的占位符都会触发启动失败。如果你只需要对特定属性做严格校验,建议使用第二种方案。

方案二:针对特定配置属性的自定义校验

如果你需要更精细的控制(只校验@ConfigurationProperties类中的某些属性),可以创建一个自定义校验注解,利用Spring的Environment来检查占位符是否可解析。

步骤1:定义自定义校验注解

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ResolvablePlaceholderValidator.class)
public @interface ResolvablePlaceholder {
    String message() default "属性值中存在无法解析的占位符";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

步骤2:实现校验器

这个校验器会利用Spring的Environment尝试解析占位符,如果解析失败,校验就不通过。

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.core.env.Environment;

public class ResolvablePlaceholderValidator implements ConstraintValidator<ResolvablePlaceholder, String> {
    private final Environment environment;

    // 通过构造方法注入Environment(Spring 4.3+支持校验器的构造方法注入)
    public ResolvablePlaceholderValidator(Environment environment) {
        this.environment = environment;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 如果需要强制非空,可以单独添加@NotNull注解,这里根据需求调整
        }
        try {
            // 使用resolveRequiredPlaceholders方法,若有任何占位符无法解析会抛出异常
            environment.resolveRequiredPlaceholders(value);
            return true;
        } catch (IllegalArgumentException e) {
            // 占位符无法解析——校验失败
            return false;
        }
    }
}

步骤3:将注解应用到配置属性类

在你的属性类上添加@Validated,并在需要校验的字段上添加@ResolvablePlaceholder:

import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties(prefix = "example")
@Validated
public class AcmeProperties {
    @ResolvablePlaceholder
    @NotNull // 可选:如果需要强制属性非空,可添加此注解
    public String key;

    // Getter、Setter、构造方法...
}

处理字面量${}:如果你需要在属性值中包含字面量${...}(不是作为占位符),可以用反斜杠转义:\${my-literal-value}。Spring会将其解析为字面量${my-literal-value},而不会当作占位符处理,因此校验器不会标记它为错误。

为什么这比字符串检查更好:我们没有去检查值是否包含${...}(这种方式会误判合法的属性值),而是直接使用Spring自身的占位符解析逻辑来验证值是否能被完全解析。这更可靠,也和Spring处理属性的内部逻辑保持一致。

如何选择方案?

  • 如果希望对整个应用的所有占位符都做严格校验,选择方案一。
  • 如果只需要对@ConfigurationProperties类中的特定属性强制要求占位符可解析,选择方案二。

两种方案都能确保Spring在启动时快速失败,而不是静默地将无法解析的占位符作为字面量使用。

内容的提问来源于stack exchange,提问作者Christian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:36:15