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

Spring Boot中SpEL集合选择结合YAML在@ConditionalOnExpression遇错排查

问题描述

我有一个Spring Boot应用,其YAML配置包含如下特性列表:

features:
  - key: feature1
    enabled: true
    description: First feature
  - key: feature2
    enabled: false
    description: Second feature
...

希望使用@ConditionalOnExpression根据特性的key来条件初始化相关Bean。尝试了两种注解写法:

@ConditionalOnExpression("${features.?[key == 'feature1'][0].enabled}")

@ConditionalOnExpression("${features}.?[key == 'feature1'][0].enabled")

但启动时均出现相同错误:

org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'

直接将不带${}的表达式传入SpelExpressionParser.parseExpression(),并基于程序构建的特性对象列表求值时,能正常返回enabled属性值,说明表达式结构本身没问题,问题出在@ConditionalOnExpression的使用方式上。

问题原因与解决方案

核心原因

${features}会被Spring的属性解析器直接替换为配置列表的字符串化形式(例如[{key=feature1, enabled=true, description=First feature}, ...]),此时整个表达式会变成类似[{...}].?[key == 'feature1'][0].enabled。但SpEL无法将这种字符串当作集合对象解析,只能将其视为普通字符串,因此触发解析错误。

正确的SpEL写法

不要用${}直接引用集合属性,而是通过SpEL的@environment获取环境中的集合对象,再结合集合选择器:

@ConditionalOnExpression("#{@environment.getProperty('features')?.?[key == 'feature1'][0]?.enabled}")

这里的?.是安全导航运算符,避免集合为空或找不到对应feature时抛出空指针异常。

更优替代方案:自定义条件注解

如果需要频繁根据feature开关初始化Bean,自定义@ConditionalOnFeature注解会比写复杂SpEL更简洁易维护:

  1. 定义自定义注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnFeatureCondition.class)
public @interface ConditionalOnFeature {
    String key();
    boolean enabled() default true;
}
  1. 实现Condition逻辑
public class OnFeatureCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnFeature.class.getName());
        String targetKey = (String) attrs.get("key");
        boolean expectedEnabled = (boolean) attrs.get("enabled");
        
        List<Map<String, Object>> features = context.getEnvironment()
                .getProperty("features", List.class);
        
        if (features == null) {
            return false;
        }
        
        return features.stream()
                .filter(feature -> targetKey.equals(feature.get("key")))
                .findFirst()
                .map(feature -> expectedEnabled == (boolean) feature.get("enabled"))
                .orElse(false);
    }
}
  1. 使用自定义注解
@ConditionalOnFeature(key = "feature1")
// 或者指定enabled状态
@ConditionalOnFeature(key = "feature2", enabled = false)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:20:58