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();
    }
}
  1. There is a properties which value is a SpEL expression that resolves to the taskExecutor bean
my.task-executor=#{@taskExecutor}
  1. The property can be mapped to a TaskExecutor field:
@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:

  1. There is a TaskExecutor bean:
@Configuraion
public class MyConfig {
    public TaskExecutor taskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}
  1. There is a ConfigurationProperties bean:
@ConfigurationProperties(prefix = "my.config")
public class MyConfigurationProperties {
    private TasExecutor taskExecutor;
    // Getters + Setters
}
  1. There are properties for the ConfigurationProperties bean:
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:

  1. com.example.demo.PropertiesSpelTest demonstrates that one can use SpEL for regular properties which can then be bound to a complex object without any converters
  2. com.example.demo.ConfigurationPropertiesWithSpelTest (currently FAILS) demonstrates the exception when one binds a SpEL expression in a ConfigurationProperties bean field. Notice that there are no problems with binding a regular reference to another property
  3. com.example.demo.ConfigurationPropertiesWithSpelWorkaroundTest demonstrates how to define a converter that overcomes the problems with SpEL expressions for ConfigurationProperties beans

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.