Spring Boot中@ConditionalOnProperty针对数组形式配置值的生效问题咨询
我太懂你这种纠结了——本来想把校验配置写成清爽的数组形式,结果用@ConditionalOnProperty直接匹配单个值却完全不生效,只能退而求其次用键值对的配置,确实有点别扭。
先给你说清楚为啥你最初的写法不行:当你在application.yml里把my-app.checks写成数组形式(比如check-one, check-two)时,Spring会把它解析成一个字符串数组类型的属性。而@ConditionalOnProperty的默认逻辑是把整个属性值和havingValue做完全匹配,也就是它会拿整个数组去和字符串"check-one"比,显然不相等,所以条件不满足,Bean就不会被加载。
那有没有办法用数组形式的配置实现你的需求呢?当然有,给你两个实用的方案:
方案一:用@ConditionalOnExpression直接判断数组包含关系
这是最直接的方式,通过SpEL表达式来判断配置的数组里是否包含目标校验名称:
@Component @ConditionalOnExpression("'${my-app.checks:}'.contains('check-one')") public class CheckOne implements Check {}
- 表达式里的
${my-app.checks:}是给配置加了个空字符串默认值,避免当my-app.checks没配置时出现空指针问题 - 如果你需要更严谨的匹配(比如避免类似
check-one-extra这种包含check-one的误匹配),可以把数组转成集合来判断:
@ConditionalOnExpression("#{'${my-app.checks:}'.split(',') contains 'check-one'}")
不过要注意如果配置项里有空格(比如check-one, check-two),split后要trim处理,比如:
@ConditionalOnExpression("#{'${my-app.checks:}'.split(',').stream().map(String::trim).collect(java.util.stream.Collectors.toList()) contains 'check-one'}")
方案二:自定义Condition实现类(更灵活)
如果你的校验逻辑比较复杂,或者不想写太长的SpEL表达式,可以自己写个Condition来解析配置数组:
- 先自定义一个注解,用来标记需要匹配的校验名称:
import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Conditional(CheckEnabledCondition.class) public @interface ConditionalOnCheckEnabled { String value(); }
- 实现对应的Condition类,负责解析配置并判断:
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Arrays; import java.util.Map; public class CheckEnabledCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 解析自定义注解的属性,拿到当前Bean对应的校验名称 Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnCheckEnabled.class.getName()); if (attributes == null) { return false; } String targetCheck = (String) attributes.get("value"); // 读取配置中的校验数组 Environment env = context.getEnvironment(); String[] enabledChecks = env.getProperty("my-app.checks", String[].class, new String[0]); // 判断目标校验是否在启用列表中 return Arrays.asList(enabledChecks).contains(targetCheck.trim()); } }
- 最后在你的校验Bean上使用这个自定义注解:
@Component @ConditionalOnCheckEnabled("check-one") public class CheckOne implements Check {}
这种方式扩展性更强,后续新增校验类时,只需要加个注解指定名称就行,不用改核心逻辑。
另外你提到的键值对配置(check-one: true)之所以能生效,是因为每个校验项都是独立的布尔属性,@ConditionalOnProperty(value = "my-app.checks.check-one", havingValue = "true")直接匹配单个属性的布尔值,逻辑简单直接,只是配置文件会稍微繁琐一点。
内容来源于stack exchange

