NOTICE: I created a repository that demonstrates the problem
Context
One can define a SpEL expression as a value of a property which can further be mapped to a field or a method parameter with the @Value annotation. For example:
1. There is a TaskExecutor bean:
@Configuraion
public class MyConfig {
public TaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
- There is a properties which value is a SpEL expression that resolves to the
taskExecutorbean
my.task-executor=#{@taskExecutor}
- The property can be mapped to a
TaskExecutorfield:
@Component
public class MyComponent {
@Value("my.task-executor")
public TaskExecutor taskExecutor;
// ...
}
Unfortunately it doesn't work for ConfigurationProperties properties
What I propose
The ConfigurationProperties properties should resolve SpEL expressions when mapping to a @ConfigurationProperties bean. For example:
- There is a
TaskExecutorbean:
@Configuraion
public class MyConfig {
public TaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
- There is a
ConfigurationPropertiesbean:
@ConfigurationProperties(prefix = "my.config")
public class MyConfigurationProperties {
private TasExecutor taskExecutor;
// Getters + Setters
}
- There are properties for the
ConfigurationPropertiesbean:
my.config.task-executor=#{@taskExecutor}
This setup leads to the following exception:
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.springframework.core.task.TaskExecutor]
at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:119)
at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:101)
at org.springframework.boot.context.properties.bind.BindConverter.convert(BindConverter.java:93)
at org.springframework.boot.context.properties.bind.Binder.bindProperty(Binder.java:463)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:407)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:350)
... 67 more
Workaround
In order to overcome this exception one can define a String-to-TaskScheduler converter:
private record StringTaskExecutorConverter(
DefaultListableBeanFactory beanFactory
) implements Converter<String, TaskExecutor> {
@Override
public TaskExecutor convert(@SuppressWarnings("NullableProblems") String source) {
final var beanExpressionResolver = beanFactory.getBeanExpressionResolver();
if (beanExpressionResolver == null) return null;
final var expressionContext = new BeanExpressionContext(beanFactory, new SimpleThreadScope());
return (TaskExecutor) beanExpressionResolver.evaluate(source, expressionContext);
}
}
But one has to define a converter for each possible target type, which might be tedious.
Demo Project
I created a repository that demonstrates the lack of binding of SpEL expressions. There are three tests:
com.example.demo.PropertiesSpelTestdemonstrates that one can use SpEL for regular properties which can then be bound to a complex object without any converterscom.example.demo.ConfigurationPropertiesWithSpelTest(currently FAILS) demonstrates the exception when one binds a SpEL expression in aConfigurationPropertiesbean field. Notice that there are no problems with binding a regular reference to another propertycom.example.demo.ConfigurationPropertiesWithSpelWorkaroundTestdemonstrates how to define a converter that overcomes the problems with SpEL expressions forConfigurationPropertiesbeans
Comment From: quaff
It may break compatibility, for example, I want to bind SpEL expression itself instead of evaluation result.
Comment From: wilkinsona
Not supporting SpEL when binding configuration properties as a documented design choice following the discussion in https://github.com/spring-projects/spring-boot/issues/1768.
As the documentation suggests, you can use @Value with a property in its canonical form to use SpEL on a case-by-case basis. The team's position hasn't changed and we continue to recommend this approach or similar. Thanks anyway for the suggestion.