spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.15k stars 40.68k forks source link

ConfigurationProperties beans should be embeddable #42179

Closed neshkeev closed 2 months ago

neshkeev commented 2 months ago

Motivation

ConfigurationProperties has many of out-of-box implementations for different technologies, like Apache Kafka, DataSource, JMS, etc.

Software Developers often combine a bunch of technologies in a single application with results in several @ConfigurationProperties Spring Beans one for each in use technologies.

Proposal

It would be great to have a way to combine all the @ConfigurationProperties beans in a single @ConfigurationProperties Spring Bean and keep all the auto-configuration abilities at the same time

Example

There is an application.properties file with two Apache Kafka Clusters and two Data Sources:

myapp.first-cluster.spring.kafka.bootstrap-servers=first:9092
myapp.second-cluster.spring.kafka.bootstrap-servers=second:9092

myapp.first-datasource.spring.datasource.name=FirstDS
myapp.first-datasource.spring.datasource.url=jdbc:postgresql://first:5432/first
myapp.first-datasource.spring.datasource.username=first
myapp.first-datasource.spring.datasource.password=first

myapp.second-datasource.spring.datasource.name=SecondDS
myapp.second-datasource.spring.datasource.url=jdbc:postgresql://second:5432/second
myapp.second-datasource.spring.datasource.username=second
myapp.second-datasource.spring.datasource.password=second

It would be nice to be able to define a bean that reads all the properties into a single bean like this:

@ConfigurationProperties(prefix="myapp")
public class MyAppProperties {

    private KafkaProperties firstCluster;
    private KafkaProperties secondCluster;

    private DataSourceProperties firstDatasource;
    private DataSourceProperties secondDatasource;

    // getters + setters
}

So then I can inject this single bean instead of a bunch of @ConfigurationProperties beans:

@Controller
public class MyController {
    private final MyAppProperties myAppProperties;
    private MyController(MyAppProperties myAppProperties) {
        this.myAppProperties = myAppProperties;
    }
}

Alternatives

It's possible to manually construct such an bean like:

  1. The class for the app's properties:

    public class MyAppProperties {
    
    private final KafkaProperties firstCluster;
    private final KafkaProperties secondCluster;
    
    private final DataSourceProperties firstDatasource;
    private final DataSourceProperties secondDatasource;
    
    public MyAppProperties(KafkaProperties firstCluster, KafkaProperties secondCluster, DataSourceProperties firstDatasource, DataSourceProperties secondDatasource) {
        this.firstCluster = firstCluster;
        this.secondCluster = secondCluster;
    
        this.firstDatasource = firstDatasource;
        this.secondDatasource = secondDatasource;
    }
    }
  2. A @Configuration bean that construct a bean for the app's properties:

    @Configuration
    public class MyAppConfiguration {
    
    @Bean
    public MyAppProperties() {
        return new MyAppProperties(firstCluster(), secondCluster(), firstDatasource(), secondDatasource());
    }
    
    @Bean
    @ConfigurationProperties(prefix = "myapp.first-cluster.spring.kafka")
    public KafkaProperties firstCluster() {
        return new KafkaProperties();
    }
    
    @Bean
    @ConfigurationProperties(prefix = "myapp.second-cluster.spring.kafka")
    public KafkaProperties secondCluster() {
        return new KafkaProperties();
    }
    
    @Bean
    @ConfigurationProperties(prefix = "myapp.first-datasource.spring.datasource")
    public DataSourceProperties firstDatasource() {
        return new DataSourceProperties();
    }
    
    @Bean
    @ConfigurationProperties(prefix = "myapp.second-datasource.spring.datasource")
    public DataSourceProperties secondDatasource() {
        return new DataSourceProperties();
    }
    }
snicoll commented 2 months ago

Software Developers often combine a bunch of technologies in a single application with results in several @ConfigurationProperties Spring Beans one for each in use technologies.

That's the expected behavior as the scope of these are quite different, even in the scope of an application.

AFAIK flagging the four fields in MyAppProperties with @NestedConfigurationProperty should do pretty much that, except the repetition of the prefix, so something like myapp.first-datasource.name instead of myapp.first-datasource.spring.datasource.name.

I don't really understand why you'd want the prefix to be repeated.

neshkeev commented 2 months ago

If @ConfigurationProperties beans are embeddable then I can reuse the @ConfigurationProperties classes from Spring Boot to compose my app's own @ConfigurationProperties bean with an arbitrary structure that suits my needs the most.

flagging the four fields in MyAppProperties with @NestedConfigurationProperty should do pretty much that

Using NestedConfigurationProperty can do the trick, but at the same time I lose all the auto-configuration abilities. So in addition to getting a custom @ConfigurationProperties bean that might consist of Spring Boot's own @ConfigurationProperties beans I also would like to keep automatic configuration of Datasources, JMS connections and so on.

It would be nice for Spring Boot to handle the following case:

  1. a prefix of a @ConfigurationProperties bean is nested inside another @ConfigurationProperties bean
  2. the prefix of the later bean is stripped from the property keys
  3. a @ConfigurationProperty Spring bean for the former bean is created
  4. Spring Boot's auto-configuration abilities kick in and a Datasource, JMS Connection, etc. of the former bean is automatically configured as well
snicoll commented 2 months ago

Thanks, it was far from obvious to me that you expected auto-configuration to apply to those. I think the embedded bits is by far a detail compared to having auto-configuration deal with multiple beans of the same type.

I am going to close this in favor of #15732 where that feature is discussed at length.