Is it possible to have immutable (final) fields with Spring Boot's @ConfigurationProperties
annotation? Example below
@ConfigurationProperties(prefix = "example") public final class MyProps { private final String neededProperty; public MyProps(String neededProperty) { this.neededProperty = neededProperty; } public String getNeededProperty() { .. } }
Approaches I've tried so far:
@Bean
of the MyProps
class with two constructors neededProperty
argumentnew MyProps()
null
@ComponentScan
and @Component
to provide the MyProps
bean. BeanInstantiationException
-> NoSuchMethodException: MyProps.<init>()
The only way I have got it working is by providing getter/setter for each non-final field.
@ConstructorBinding. . This annotation tells Spring to bind our configuration properties using the provided constructor instead of using the setters.
Immutable class means once the object of the class is created its fields cannot be modified or changed. In Java, all the wrapper classes like Boolean, Short, Integer, Long, Float, Double, Byte, Char, and String classes are immutable classes.
@ConfigurationProperties allows to map the entire Properties and Yaml files into an object easily. It also allows to validate properties with JSR-303 bean validation. By default, the annotation reads from the application.
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
@Service public 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With