Is there a recommended way to introduce restructurings/renamings into an externalized configuration while keeping backwards compatibility for consumers still relying on the old configuration structure?
For example, given a library used the following configuration structure defined via @ConfigurationProperties
in the past:
old-properties: an: old-property: true another: custom-property: 1234
A new version of that library redefines the configuration to something like this:
my-library: a-property: true another-property: 1234
Is there a good way to deprecate the old structure while keeping compatibility for existing consumers for some time? Consumers using the new version of the library should still be able to use old-properties.an.old-property
and have that automatically mapped to my-library.a-property
.
I’m aware of the functionality to use additional configuration metadata to mark a property as deprecated, but I’m explicitly looking for a way to support both versions to ease migration.
Advertisement
Answer
I looked into how Spring Boot handled the deprecation phase for logging.file
(which was replaced by logging.file.name
) and as they implemented the fallback directly in code I decided to try something similar by creating the new @ConfigurationProperties
in a @Bean
method which handles setting the values from old property names if available.
Given the new configuration structure looks like this (using Lombok for brevity):
import lombok.Data; @Data public class MyLibraryConfigurationProperties { private String aProperty; private String anotherProperty; }
The @Bean
method now takes care of reading the old value and applying it to the properties:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class MyLibraryConfiguration { @Bean @ConfigurationProperties(prefix = "my-library") public MyLibraryConfigurationProperties myLibraryConfigurationProperties(Environment environment) { MyLibraryConfigurationProperties config = new MyLibraryConfigurationProperties(); // fallback to old property if available if (environment.containsProperty("old-properties.an.old-property")) { // here we could also log warnings regarding the deprecation config.setAProperty(environment.getProperty("old-properties.an.old-property")); } return config; } }
If the new value is also set via config, it will override the value set from the old property.