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