A Spring Boot application typically consists of several components handling the business functionality and probably some configuration to configure all these components. This configuration consists of defining some properties, setting up some beans with the right conditions and dependencies and wrapping it all together into a class structure. Nevertheless, the configuration of our Spring Boot application is also code. Let’s threat is as code.

In this blog we will se how we can improve our Spring Boot Configuration by splitting up the configuration from the properties and how this effects the design principles.

If your configuration is just code, let’s treat is as code.

For object oriented software development Robert C. Martin described five design principles. Let’s shortly look at the SOLID design principles:

  1. Single Responsibility Principle

  2. Open/Closed Principle

  3. Liskov Substitution Principle

  4. Interface Segregation Principle

  5. Dependency Inversion

Before the separation

@Configuration (1)
@ConfigurationProperties("nl.jdriven.blog.example") (2)
public class MyConfiguration {

    private String duration; (3)

    public String setDuration(String duration) {
        this.duration = duration;
    }

    public String getDuration() {
        return this.duration;
    }

    @Bean
    MyBean myBean() {
        return new MyBean(getDuration()); (4)
    }

    @Bean
    @ConditionalOnBean(DataSource.class)
    AnotherBean anotherBean(DataSource dataSource) {
        return new AnotherBean(dataSource);
    }
}
1 Tell the Spring context this is a Configuration bean with Bean factory methods
2 Map properties with a certain prefix
3 The property nl.jdriven.blog.example.duration with the corresponding getter and setter
4 Configures a bean through constructor injection with the value of duration

The demonstrated class MyConfiguration is a mix of a Spring configuration class, where beans are (conditionally) configured, and a Spring properties class, where effective externalized (environment) properties are managed. This means that the design principle Single Responsibility Principle is not met very well, the classes have multiple responsibilities.

Yes, configuration properties are also a kind of configuration. In my opinion they are slightly different from a general configuration. A general configuration class has the intention of creating beans in your application context, where a configuration properties class is meant to manage the externalized properties.

After the separation

It’s better to split up these two responsibilities and improve the open/closed principle.

@ConfigurationProperties("nl.jdriven.blog.example") (1)
@ConstructorBinding (2)
public class MyProperties {

    @NotNull
    private final String duration; (3)

    MyProperties(String duration) { (4)
        this.duration = duration;
    }

    public String getDuration() { (5)
        return this.duration;
    }
}
1 The ConfigurationProperties sticks to the properties with the corresponding prefix
2 Create immutable properties classes through constructor injection.
3 Make duration final and add a javax validation @NotNull (or any other validation)
4 The constructor with all required properties
5 Only a getter for the duration, no setter anymore

As you see in the example above, we have a separate immutable properties class with a defined prefix nl.jdriven.blog.example. The property values cannot change after Spring has constructed the MyProperties class and registered it as Bean in the ApplicationContext. In order for this to work, we need to set the @ConstructorBinding property. More on constructor binding in Spring properties can be found here

@Configuration
@EnableConfigurationProperties(MyProperties.class) (1)
class MyConfiguration { (2)

    @Bean
    MyBean myBean(MyProperties myProperties) { (3)
        return new MyBean(myProperties.getDuration());
    }

    @Bean
    @ConditionalOnBean(DataSource.class)
    AnotherBean anotherBean(DataSource dataSource) {
        return new AnotherBean(dataSource);
    }
}
1 Enable our new MyProperties by adding this @EnableConfigurationProperties to this class
2 This class can be package protected (open/closed principle)
3 The bean of MyProperties can be injected as a bean

The MyProperties can be injected like any other bean, for example into the factory method of myBean. The class MyConfiguration is much cleaner and has one Single Responsibility: conditionally configure beans. Furthermore, this class can be closed for extension and for modification. Configuration classes should never be extended, but should be driven by conditional beans. For example: constructing a bean only when it does not exist yet.

shadow-left