Spring Boot - Developer Properties

In many of my projects there is the requirement to provide what I call “developer properties”. A developer properties file holds configuration values only being applied for a specific user, e.g. andre.properties would be applied when JVM would have been started by a user with name andre.

As an additional requirement, the developer properties should be added with high priority. Its settings will overwrite properties from other property holders.

The way such a requirement can be implemented is by having a look at interface org.springframework.boot.env.EnvironmentPostProcessor. This interface allows for customization of the application’s Environment which in turn holds the property sources (= places properties come from). Classes implementing EnvironmentPostProcessor have to be registered in a file called META-INF/spring.factories:

# EnvironmentPostProcessors
org.springframework.boot.env.EnvironmentPostProcessor=com.exactag.uisetup.config.DeveloperConfigurationPostProcessor

The information found in spring.factories is used by Spring in a very early phase of the DI container startup and its possible to register multiple interface implementations too (separated by ‘,’). This file will be automatically picked up by Spring when located in src/main/resources (in our Gradle build).

Once the implementation class is registered, we can add the actual logic and define an implementation for the postProcessEnvironment method (the only method of this interface):

import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.support.ResourcePropertySource

class DeveloperConfigurationPostProcessor : EnvironmentPostProcessor {

    override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {

        // is the dev profile active?
        if (!environment.acceptsProfiles("dev")) return

        // get the developer user name from another configuration property
        val developer = environment.getProperty("config.developer") ?: return

    	// if the user name is available, try to load his/her properties file
        val developer = environment.getProperty("config.developer") ?: return
        val developerProperties = ClassPathResource("/config/developer/$developer.properties").takeIf { it.exists() } ?: return

        // adds this new property source with highest precedence
        val mutablePropertySources = environment.propertySources
        mutablePropertySources.addFirst(ResourcePropertySource(developer, developerProperties))
    }
}

First of all, we do a check whether the dev profile is active or not. If it is active, we will read the config.developer property from the configuration properties. In our case, the default is configured as:

config.developer=${user.name}

${user.name} refers to the system property user.name which holds the current user name. As Spring Boot has a SystemPropertyPlaceholderResolver it will resolve system properties in placeholders like in the case above.

Summary

This article showed a way how to add so-called “developer properties” files to Spring Boot projects. A developer properties file is a file with user-specific settings which will have highest priority over any other configuration properties.