Immutable @ConfigurationProperties
From Spring Boot 2.2, it is at last possible to define an immutable class decorated with @ConfigurationProperties
.
The documentation shows an example.
You just need to declare a constructor with the fields to bind (instead of the setter way) and to add the @ConstructorBinding
annotation at the class level to indicate that constructor binding should be used.
So your actual code without any setter is now fine :
@ConstructorBinding@ConfigurationProperties(prefix = "example")public final class MyProps { private final String neededProperty; public MyProps(String neededProperty) { this.neededProperty = neededProperty; } public String getNeededProperty() { .. }}
I have to resolve that problem very often and I use a bit different approach, which allows me to use final
variables in a class.
First of all, I keep all my configuration in a single place (class), say, called ApplicationProperties
. That class has @ConfigurationProperties
annotation with a specific prefix. It is also listed in @EnableConfigurationProperties
annotation against configuration class (or main class).
Then I provide my ApplicationProperties
as a constructor argument and perform assignment to a final
field inside a constructor.
Example:
Main class:
@SpringBootApplication@EnableConfigurationProperties(ApplicationProperties.class)public class Application { public static void main(String... args) throws Exception { SpringApplication.run(Application.class, args); }}
ApplicationProperties
class
@ConfigurationProperties(prefix = "myapp")public class ApplicationProperties { private String someProperty; // ... other properties and getters public String getSomeProperty() { return someProperty; }}
And a class with final properties
@Servicepublic class SomeImplementation implements SomeInterface { private final String someProperty; @Autowired public SomeImplementation(ApplicationProperties properties) { this.someProperty = properties.getSomeProperty(); } // ... other methods / properties }
I prefer this approach for many different reasons e.g. if I have to setup more properties in a constructor, my list of constructor arguments is not "huge" as I always have one argument (ApplicationProperties
in my case); if there is a need to add more final
properties, my constructor stays the same (only one argument) - that may reduce number of changes elsewhere etc.
I hope that will help
In the end, if you want an immutable object you can also "hack" the setter that is
@ConfigurationProperties(prefix = "myapp")public class ApplicationProperties { private String someProperty; // ... other properties and getters public String getSomeProperty() { return someProperty; } public String setSomeProperty(String someProperty) { if (someProperty == null) { this.someProperty = someProperty; } }}
Obviously if the property is not just a String, that is a mutable object, things are more complicated but that's another story.
Even better you can create a Configuration container
@ConfigurationProperties(prefix = "myapp")public class ApplicationProperties { private final List<MyConfiguration> configurations = new ArrayList<>(); public List<MyConfiguration> getConfigurations() { return configurations }}
where now the configuration is a clas without
public class MyConfiguration { private String someProperty; // ... other properties and getters public String getSomeProperty() { return someProperty; } public String setSomeProperty(String someProperty) { if (this.someProperty == null) { this.someProperty = someProperty; } }}
and application.yml as
myapp: configurations: - someProperty: one - someProperty: two - someProperty: other