Home > Software design >  Spring Boot: using SpEL collection selection from YAML in @ConditionalOnExpression
Spring Boot: using SpEL collection selection from YAML in @ConditionalOnExpression

Time:09-27

I have a Spring Boot application with a YAML configuration that contains a feature list like this:

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

I would like to use @ConditionalOnExpression to conditionally initialize beans related to those features, identifying them by keys. Since "features" property is a list, it seems I need collection selection to do this. I have tried these two options for the annotation's value:

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

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

But both give the same error on startup:

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

If I pass the expression (without ${}) to SpelExpressionParser.parseExpression() and then evaluate it (against a list of feature objects built programmatically), it works as expected and returns the value of "enabled" property. So the expression's structure seems to be OK, and the problem is how I use it in @ConditionalOnExpression. What exactly can I be doing wrong?

CodePudding user response:

${...} has nothing to do with SpEL; that syntax is for property placeholders in Spring.

e.g. @Value("${property.foo:default}" String someProperty.

This will set someProperty to the value of property.foo if present, or default otherwise. (the :... part can be omitted to force the property to exist).

CodePudding user response:

Spring SpEL supports only simple property placeholders inside the properties file.

If your properties file and condition code are like this, your SpEL will work:

features:
  feature1:
    enabled: true
    description: First feature.
  feature2:
    enabled: false
    description: Second feature.
@ConditionalOnExpression(
        value = "#{'${features.feature1.enabled}'.equals('true')}"
)
But if you want to load it as a map feature and use the map SpEL features;

The most elegant way is writing the key-values as an inline JSON (use ' and " chars to avoid cumbersome escaping) and parsing it using SpEL:

features: '{
"feature1": {"enabled":"true", "description":"..."},
"feature2": {"enabled":"false", "description":"..."}
}'
@ConditionalOnExpression(
        value = "#{${features}['feature1']['enabled'].equals('true')}"
)

If you had used the selection method it would have looked like this;

@ConditionalOnExpression(
        value = "#{${features}.?[key=='feature1']['feature1']['enabled'].equals('true')}"
)
  • Related