Issue
I have a Spring boot configuration YAML with something like
spring:
application:
name: my-app
a: a literal
b: <<external due to special first and last chars>>
What i'm trying to do is to add some kind of resolver that will detect that the value of b
is of the form <<X>>
and will trigger retrieving that value from an external rest api to overwrite in memory the value that was in the YAML before it gets passed to the bean that holds the configurations at runtime
I tried and failed using an EnvironmentPostProcessor
because I can't get ahold of the actual property values, just the property sources, so I can't post-process the values.
What currently works for me is in the @Configuration
bean that holds the fields a
and b
, implement something in the setters to detect if the value that spring is trying to set starts with <<
and ends with >>
and if so, overwrite what gets loaded into the pojo with the version that i retrieve from the rest api. This is not ideal because I end up with a lot of duplication
What's the right way to implement something like this in Spring 5? I know spring properties support references to other properties using the syntax ${a}
so there must be some mechanism that already allows to add custom placeholder resolvers
Solution
I ended up changing things a bit to mark the special properties. Then I created my own PropertySource
kind of like @Andreas suggested. It was all inspired by org.springframework.boot.env.RandomValuePropertySource
The trick was changing the special chars <<
and >>
to the syntax used already by spring: ${}
, but like the random resolver which uses ${random.int
} I did something like ${rest.XXX}
. What I didn't know before is that by doing that, Spring will invoke all the property sources a second time with a new property name coming from the placeholder value (rest.XXX
in my previous example). This way in the property source I can handle the value if the name of the property starts with my prefix rest.
Here is a simplified version of my solution
public class MyPropertySource extends PropertySource<RestTemplate> {
private static final String PREFIX = "rest.";
public MyPropertySource() {
super(MyPropertySource.class.getSimpleName());
}
@Override
public Object getProperty(@Nonnull String name) {
String result = null;
if (name.startsWith(PREFIX)) {
result = getValueFromRest(name.substring(PREFIX.length()));
}
return result;
}
}
Finally, to register the property source I used an EnvironmentPostProcessor
as described here. I couldn't find a simpler way that doesn't entail maintaining a new file META-INF/spring.factories
Answered By - Hilikus