Managing Application Properties in Spring Boot

Many projects deal with application-wide parameters, mostly called properties. These are inputs to the program, that wouldn’t normally be changed. They do however provide for simple setup in multiple circumstances.

When to Use Properties

Good example situations in which properties would come in handy are database and email setups. If your application sends out mails, it will need to set some parameters, like protocol, host and port. If authentication is involved, properties should cover them as well. In your emailing implementation you would then read out the property values and handle them however required.

Mail properties
Some application properties for outgoing email for local development.

There are many advantages of setting these parameters in one centralized file. One of which is that quick adjustments can easily be made, without touching the code (that is, when only values are edited). When an application is deployed on multiple environments (local development, acceptance, staging, production,…) it would be enough to adjust properties in accordance with the environment on which the app is running on.

 

Reading Properties in Spring

When it comes down to extracting the values from the property file(s) when using Spring, you have a few options.

The first option, which is also the quickest solution, is to use the @Value annotation on class members or directly in an autowired constructor, as a parameters.

@Value("${my.property.name}")
private String myProperty;
@Autowired
 public MyConstructor(@Value("${my.property.name}") final String property) {
     this.property = property;
 }

Just pass your property key to the annotation. That’s it, no need to use any file reader, Spring deals with that for you. However this method is very quick and easy to implement, I’d like to point out that whenever you decide to edit the property key, you would also have to adjust it in the annotation value in code. And that would be for every annotation that reads out the edited property in question. This could obviously be made a bit more efficient and future proof.

Read Once, Access Anytime

A more solid approach would be to read all the properties (ideally when the application is launched) and put them into memory, so they would be accessible throughout the entire lifecycle of the program. Spring makes this incredibly easy, so this is by far the recommended approach.

Example class with properties in memory.
Example class with properties in memory.

In the image above, you can see an example of a class containing application properties. It contains no business logic whatsoever. The class must be annotated with Spring’s @ConfigurationProperties annotation. You can pass a prefix value (the prefix of the property key) if so required. The nested classes cover the nested properties. The protocol property in the Mail class would be the described as cs.mail.protocol in the properties file in this case. If a property key requires editing in the future, only the key and its respective property should be adjusted in code, never more, never less.

The @Data annotation (Lombok) generates all the necessary getters and setters and it reduces boilerplate code at the same time (oh, and it makes you develop that little bit faster). You could of course provide the getters and setters yourself if you don’t want to use Lombok.

Additional Spring Configuration

Not much more left to do in order to make the properties accessible in any spring component. To follow the previous example, I’d like to bring in an example Spring configuration class:

@Configuration
@EnableConfigurationProperties({ CSProperties.class })
public class PropertiesConfig {

    private final CSProperties properties;

    @Autowired public PropertiesConfig(CSProperties properties) {
        this.properties = properties;
    }

    @Bean
    public CSProperties.Database getDatabase() {
        return properties.getDb();
    }

    @Bean
    public CSProperties.Mail getMail() {
        return properties.getMail();
    }

    @Bean
    public CSProperties.Background getBackground() {
        return properties.getBackground();
    }

    @Bean
    public CSProperties.Cache getCache() {
        return properties.getCache();
    }

What this class does is simply creating beans of the property elements. It injects our base properties class in the constructor and it creates beans of the inner classes, to make them injectable anywhere. The useful aspect of this implementation is the fact that whenever you autowire one of the subclasses, you don’t get all the other properties you don’t need. Basically you just need to grab the properties that are in scope of whatever functionality you’re developing. Obviously you can also grab al the properties whenever needed.

Since our properties class is statefull and it has getters as well as setters, we are also free to change the state of the property values while running the application. This can result in totally different application behaviour, so be careful with it. Perhaps you should add some security checks for the setters access.

To wrap things of, an example extract of a database configuration class that only injects the database properties (other properties aren’t necessary here):

private final CSProperties.Database dbProps;

    @Autowired public MyBatisConfig(CSProperties.Database dbProps) {
        this.dbProps = dbProps;
    }

    @Bean
    public DataSource dataSource() {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource ();
        dataSource.setDriverClass(com.mysql.cj.jdbc.Driver.class);
        dataSource.setUrl(dbProps.getUrl());
        dataSource.setUsername(dbProps.getUsername());
        dataSource.setPassword(dbProps.getPassword());

        return dataSource;
    }

Bonus: Property Metadata

Code completion and documentation above all. Spring actually makes it possible to attach custom metadata to your properties. IntelliJ actually detects when a property key has metadata, if so, it will provide code completion and additional information. You can also define the datatypes and default values.

Property metadata file.
Property metadata file.

Have your IDE generate a JSON file in your META-INF folder. The file will be called ‘additional-spring-configuration-metadata.json’. If you decide to use such a file, make sure to update it along with the edited property file(s).