Reputation: 2516
Java 16 introduced Records, which help to reduce boilerplate code when writing classes that carry immutable data. When I try to use a Record as @ConfigurationProperties
bean as follows I get the following error message:
@ConfigurationProperties("demo")
public record MyConfigurationProperties(
String myProperty
) {
}
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.demo.MyConfigurationProperties required a bean of type 'java.lang.String' that could not be found.
How can I use Records as @ConfigurationProperties
?
Upvotes: 54
Views: 37121
Reputation: 1176
I use Spring boot 3.2.2
, so I hadn't to to add most of the things mentioned in another answers
Here is what I did:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({KafkaProperties.class})
public class AppConfig {
public static final String KAFKA = "kafka";
}
@ConfigurationProperties(prefix = AppConfig.KAFKA)
public record KafkaProperties(String autoStartup,
Topics topics) {
public record Topics(String name) {
}
}
That's all I need to add and everything if working fine.
Note: need to have naming consistency in Java and application.prop/yaml file
Upvotes: 1
Reputation: 41
In Spring-Boot 2.7.5 you will only have to
@ConfigurationProperties
@ConfigurationPropertiesScan({RecordConfig.class})
. This part is important as this tells Spring the specifics of the Config class and helps it register as a beanenter code here
.Upvotes: 0
Reputation: 1454
ConstructorBinding
and compact form of constructor in Spring Boot 3.1.0As of 'spring-boot' version '3.1.0':
org.springframework.boot.context.properties.ConstructorBinding
is deprecated and can't be applied to type declaration (class, interface, enum, or record), but can be applied to constructor declarationorg.springframework.boot.context.properties.bind.ConstructorBinding;
can't be applied to type declaration (class, interface, enum, or record), but can be applied to constructor declarationThus, we can declare record
and apply org.springframework.boot.context.properties.bind.ConstructorBinding
to the compact form of constructor which is still looks concise:
@ConfigurationProperties
public record ApplicationProperties(/* Your properties here. E.g.:
String prop1,
Integer prop2,
NestedConfig nestedProps
*/) {
@ConstructorBinding
public ApplicationProperties {
}
}
Note: Don't forget to enable support for @ConfigurationProperties
annotated beans in your application. E.g.:
@EnableConfigurationProperties(ApplicationProperties.class)
@SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
Upvotes: 8
Reputation: 11
When mapping YAML configuration using the class approach, there is no problem if you use the @Configuration annotation.
However, when switching to using record for mapping, Spring will inform you that it is a final type and cannot be proxied and managed in the container.
I have found that you can explicitly declare in the main class of Spring using @EnableConfigurationProperties(XXProperties.class) or @ConfigurationPropertiesScan that you need to add the class with the @ConfigurationProperties annotation to the container, regardless of whether it is a class or record.
In version 2.x of Spring Boot, if you want to use the record type to map YAML files, you need to add the annotation @ConstructorBinding to the class. However, in version 3.0 of Spring Boot, this is no longer necessary and it has been marked as deprecated.
@SpringBootApplication
//@EnableConfigurationProperties(YmlConfigDbProperties.class)
@ConfigurationPropertiesScan
public class AppMain {
public static void main(String[] args) {
SpringApplication.run(AppMain.class, args);
}
}
@ConfigurationProperties(prefix = "config.db")
//@Configuration
public record YmlConfigDbProperties(){}
Upvotes: 1
Reputation: 175
With spring boot 3.0.4, I have been able to use the records directly without any @ConstructorBinding annotation.
@ConfigurationProperties(prefix = "app")
@Validated
public record ApplicationProperties(String version, FtpProperties ftp) {
public record FtpProperties(
@NotEmpty String host,
@Positive int port,
@NotEmpty String username) {}
}
Upvotes: 5
Reputation: 6075
I had the same issue with spring boot 3.0.0 and @ConstructorBinding
is deprecated.
I needed to add:
@SpringBootApplication
@ConfigurationPropertiesScan
public class Application
Upvotes: 16
Reputation: 2516
Answering my own question.
The above error raises from Spring Boot not being able to construct the bean because of the lack of a no-argument constructor. Records implicitly declare a constructor with a parameter for every member.
Spring Boot allows us to use the @ConstructorBinding
annotation to enable property binding by constructor instead of setter methods (as stated in the docs and the answer to this question). This also works for records, so this works:
@ConfigurationProperties("demo")
@ConstructorBinding
public record MyConfigurationProperties(
String myProperty
) {
}
Update: As of Spring Boot 2.6, using records works out of the box and @ConstructorBinding
is not required anymore when the record has a single constructor. See the release notes.
Upvotes: 72
Reputation: 6380
If you need to declare default values programatically:
@ConfigurationProperties("demo")
public record MyConfigurationProperties(String myProperty) {
@ConstructorBinding
public MyConfigurationProperties(String myProperty) {
this.myProperty = Optional.ofNullable(myProperty).orElse("default");
}
}
java.util.Optional
properties:
@ConfigurationProperties("demo")
public record MyConfigurationProperties(Optional<String> myProperty) {
@ConstructorBinding
public MyConfigurationProperties(String myProperty) {
this(Optional.ofNullable(myProperty));
}
}
@Validated
and java.util.Optional
in combination:
@Validated
@ConfigurationProperties("demo")
public record MyConfigurationProperties(@NotBlank String myRequiredProperty,
Optional<String> myProperty) {
@ConstructorBinding
public MyConfigurationProperties(String myRequiredProperty,
String myProperty) {
this(myRequiredProperty, Optional.ofNullable(myProperty));
}
}
Based on this Spring Boot issue.
Upvotes: 7