How can one map an application.yaml
configuration with nested properties to a similar record structure in Java?
Eg, if we have the following yaml:
foo:
bar:
something: 42
baz:
otherThing: true
color: blue
The desired record structure would be something along the lines of:
@ConfigurationProperties(prefix = "foo")
@ConstructorBinding
public record Foo(
Bar bar,
Baz baz,
String color
) {}
// ---
@ConfigurationProperties(prefix = "foo.bar")
@ConstructorBinding
public record Bar(
int something
) {}
// ---
@ConfigurationProperties(prefix = "foo.baz")
@ConstructorBinding
public record Baz(
boolean otherThing
) {}
You don't need @ConfigurationProperties
for each nested class. It only for the root class (Foo.class). Then make the Foo as Spring Bean by inserting @Component
above the class or put @ConfigurationPropertiesScan
on the Application class.
Turns out I didn't ask the correct question for the issue I had:/ So for the case people find this topic from a similar issue, the answer to my actual issue follows here.
The problem arises with a nested yaml trying to "short cut" on the model hierarchy, so given the following yaml:
foo:
bar:
baz:
bum: "hello"
I was trying to model the hierarchy as follows:
@ConfigurationProperties(prefix = "foo")
@ConstructorBinding
public record Foo(BarBaz barBaz) {}
// ---
@ConfigurationProperties(prefix = "foo.bar.baz")
@ConstructorBinding
public record BarBaz(String bum) {}
Here the problem arises that Foo
cannot do constructor binding for BarBaz
(not sure why). So there are two possible solutions that I found:
1. Do the full modelling (decided that this is what I prefer)
That is, don't try to skip the middle model for bar
.
@ConfigurationProperties(prefix = "foo")
@ConstructorBinding
public record Foo(Bar bar) {}
// ---
@ConfigurationProperties(prefix = "foo.bar")
@ConstructorBinding
public record Bar(Baz baz) {}
// ---
@ConfigurationProperties(prefix = "foo.bar.baz")
@ConstructorBinding
public record Baz(String bum) {}
2. Don't use @ConstructorBinding
when embedding more nestings
Simply skip the constructor binding in Foo
.
@ConfigurationProperties(prefix = "foo")
public record Foo(BarBaz barBaz) {}
// ---
@ConfigurationProperties(prefix = "foo.bar.baz")
@ConstructorBinding
public record BarBaz(String bum) {}
Although simpler, it's less consistent.
I think for simplification you can just create a single file with:
@ConfigurationProperties(prefix = "foo")
public record Foo(Bar bar) {
public record Bar(Baz baz) {
public record Baz(String bum) {}
}
}
and this is working fine in spring-boot and you don't need to repeat annotations and when using it you will just use:
String bumVal = foo.bar().baz().bum();
where foo is just injected in your Bean(s) where you need it.
I removed even the @ConsructorBinding
as since spring-boot 2.6 it is no more needed as long the record defines only one constructor, see release notes .
The above configuration relates to your own answer structure, but here is also the compact way for the original questions structure:
@ConfigurationProperties(prefix = "foo")
public record Foo(Bar bar, Baz baz, String color) {
public record Bar(String something) {
}
public record Baz(String otherThing) {
}
}
I found the record
type very useful for this use case as very compact and not too much code to write.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.