Allow @ConfigProperty default value definition by assigning value to field
Currently when injecting a config property directly with @ConfigProperty I have to define a default value for within the annotation.
For me, there are two flaws with that
- type safety is lost
- when running simple unit tests, the field is null even if it theoretically has a default value
It would be nice, if on inspection, an assigned value would treated as a default value.
@ConfigProperty(name = "storage.freespace.min")
long minimumFreeSpaceMb = 10_000;
Usually, initializing an injected member is not recommended, but nothing explicitly forbids that. If I remember correctly, nothing in the CDI API allows you to retrieve such values, so achieving this may require some workarounds.
My recommendation is to use @ConfigMapping. While you still miss type safety at compile time, it will report conversion issues at runtime, and because it doesn't require CDI, it works with simple unit tests.
If this does not suit your needs, feel free to send a PR and I'm happy to review it.
@HerrDerb I like the idea very much, but more explicitly:
There must be constants similar to
org.eclipse.microprofile.config.inject.ConfigProperty#UNCONFIGURED_VALUE
e.g.
public @interface ConfigProperty {
/** default value is absent */
String UNCONFIGURED_VALUE = "org.eclipse.microprofile.config.configproperty.unconfigureddvalue";
/** default Value is null */
String NULL_VALUE = "org.eclipse.microprofile.config.configproperty.nullvalue";
/** default Value must be read from the annotated field (regardless of field's value: null, 0, etc.) */
String FIELD_VALUE = "org.eclipse.microprofile.config.configproperty.fieldvalue";
/** default value's default must be read from application config ~ smallrye.config.configproperty.default ~ and if absent use UNCONFIGURED_VALUE. So one can change it to FIELD_VALUE with full backward compatibility */
String CONFIG_VALUE = "org.eclipse.microprofile.config.configproperty.configvalue";
String name() default "";
String defaultValue() default UNCONFIGURED_VALUE;
}
See also https://github.com/smallrye/smallrye-config/issues/1091
To clarify, we don't control the @ConfigProperty API, since it comes from MicroProfile Config: https://github.com/eclipse/microprofile-config
I'm not a big fan of the @ConfigProperty annotation because it requires a CDI Bean for injection to work.
Configuration should not require CDI to exist, and for that reason, we added @ConfigMapping and a programmatic API that allows to retrieve mappings, so users can use Configuration standalone, with type-safe declarations. It also works with plain JUnit tests.
@radcortez I love your programmatic API. With one BeanPostProcessor I even use it from Spring (not Boot).
Your API allows precise control of what to include and there is no strange mix of Spring's Environment and PropertySourcesPlaceholderConfigurer.
Again I can't speak about Spring Boot, but @ConfigMapping works better than raw-Spring's @ConfigurationProperties
I could only propose to add 3 converters:
.withConverter(DateFormat.class, 101, SimpleDateFormat::new)
.withConverter(DateTimeFormatter.class, 101, DateTimeFormatter::ofPattern)
.withConverter(CharSequence.class, 101, s->s)
@Bean
public static SmallRyeConfig smallRyeConfig (
@Autowired @NonNull ConfigurableEnvironment env
){
val ps = new SmallRyeConfigPropertySource();
env.getPropertySources()
.addLast(ps);
return config();
}
public class SpringSmallRyeConfigPropertySource extends EnumerablePropertySource<SmallRyeConfig> {
SpringSmallRyeConfigPropertySource (){
super("SmallRyeConfig", config());
}//new
@Override
public @Nullable String getProperty (String key){
return source.getRawValue(key);//== getConfigValue(name).getValue(), not raw
}
@Override
public boolean containsProperty (String key){
return source.getRawValue(key) != null;
}
@Override
public String[] getPropertyNames (){
return Iterables.toArray(source.getPropertyNames(), String.class);
}
}
Injection works also almost as proposed
I guess you are required to add @ConfigProperty handling on the Spring side, or does Spring already offer that on its own (I believe it doesn't)?
Spring doesn't support anything external out of the box, of course: they have their own annotations for everything :-) But one can add processing of any annotation. More about it here: https://github.com/smallrye/smallrye-config/issues/1091#issuecomment-1893710049