RFE: example needed of a spring.config.import factory that can read properties from the parent doc.
I am trying to create a spring.config.import factory like spring-cloud-config-client that is invoked similarly:
# application.properties
key1 = value1
key2 = value2
spring.config.import = myfactory:
i.e just like spring-cloud-config-client
# application.properties
spring.cloud.config.username = johndoe
spring.cloud.config.password = johnpassword
spring.config.import = configserver:
In the factory I want to read properties key1/key2 (e.g. host/port username/password) - however spring-cloud-config-client does not do this in a simple manner.
- if the properties were higher up in the hierarchy then
binder.bind()works; from the parent doc - I always get an exception; I guess this is tricky because the parent triggered the factory but I want the factory to read existing data from the parent - instead of reading property values simply, spring-cloud-config-client has enormous ceremony of creating a
@ConfigurationPropertiessidekick, instantiating it withbind()/registerIfAbsent(), then reading the properties from the sidekick.
Is there a more straightforward technique without having to create this parallel object? spring-cloud-kubernetes also does this sidekick ceremony.
Comment From: philwebb
Thanks for the suggestion, but I think this use-case is a little too specific to include in the reference documentation, especially as it covers a number of areas of the codebase.
I'll provide an answer here instead in case anyone else has a similar problem or we want to reconsider adding the documentation later.
If you want to read a single value, you can bind it directly to a scalar type. For example, you can do the following to get key1 as a String:
context.getBinder().bind("key1", String.class).orElse(null)
Probably even better would be to group related keys using the same prefix and then bind to a map. For example, if you change your properties to:
my.key1 = value1
my.key2 = value2
You can do:
Map<String, String> keys = context.getBinder().bind("my", Bindable.mapOf(String.class, String.class))
.orElse(Collections.emptyMap());
String key1 = keys.get("key1");
String key2 = keys.get("key2");
If things get more complex, then having a companion object would be best, but it doesn't need to be a public class.
I've written a small demo app and pushed it to https://github.com/philwebb/scratch-boot-gh-32854 that shows the Map binding approach.
Comment From: aalba6675
@philwebb thank you - I mistakenly used the Binder that can be constructor injected instead of getting the binder from the context:
MyResolver(Log log, Binder, binder) {
super();
this.log = log;
this.binder = binder;
}
Could you explain what this Binder is used for (I mean vs context.getBinder() in an overriden method)?