Proposed idea to @Value annotation
Today the typical use of @Value annotation is:
@Value("my.property")
private String myProperty;
But properties is never validated on load. And a lot of boilerplate code is used to validate if properties is valid or not and of the correct type. An my view is that the configuration is either valid or not And if it is not valid, don't bother to start application.
I would like to propose an optional extra parameter to the annotation
Namely "type"
so
public interface TypeAdapter<T> {
public T convert(String value);
}
Example
@Value("my.property", new LongTypeAdapter())
private Long myProperty;
The typeadapter will only be called if the String value is not null and the typeAdapter instance is not null.
A requirement must be that field class must be of the same type as generic type T in TypeAdapter
This can easily give a a long list of TypeAdapter implementations
- IntegerTypeAdapter
- FileTypeAdapter
- DirectoryTypeAdapter
- HostTypeAdapter
- EmailTypeAdapter
- ListTypeAdapter
- CertificateFileFileAdapter
- EnumTypeAdapter
- .....
There by have way to know that configuration is valid at startup.
Im no expert in annotation and I have tried to finde the @Value AnnotationProcessor to make a more complete example, but without success.
An optional way to do it is to make a new annotation.
@ValueTyped(value,type)
So not to mess with the existing @Value annotation.
Comment From: rockwool73
@Value(String value, TypeAdapter<T>)
private T value
```;
**Comment From: rockwool73**
@Value(String value, TypeAdapter
**Comment From: mdeinum**
What is wrong with
```java
@Value("${my.value}")
private long value;
Conversion is already supported so not sure what this would add?
Comment From: rockwool73
Well it is just simple types. But to have a way to know configuration is valid. Before we wants the application start up at all
Here are some example of TypeAdapters I could imagine, with some @Value examples at the bottom Many more I would think people would come up with
public class FileTypeAdapter implement TypeAdapter<File> {
public TypeAdapter(boolean mustExists, String extension) { ... }
}
public class DirectoryTypeAdapter implement TypeAdapter<File> {
public TypeAdapter(boolean mustExists, boolean mustBeEmpty) { ... }
}
public class IpRangeTypeAdapter implement TypeAdapter<IpRangeVO> {//Ex 172.60.47.48 or 172.45.*.* or 187.45-88,30.*
public IpRangeTypeAdapter() { ... }
}
public class EmailTypeAdapter implement TypeAdapter<String> {
public EmailTypeAdapter() { ... }
}
public class LdapNameTypeAdapter implement TypeAdapter<String> {
public LdapNameTypeAdapter() { ... }
}
public class URLTypeAdapter implement TypeAdapter<URL> {
public URLTypeAdapter() { ... }
}
public class PasswordTypeAdapter implement TypeAdapter<PasswordVO> {
public PasswordTypeAdapter(PasswordMethod method) { ... }
}
public class EnumTypeAdapter<T extends Enum<T>> implement TypeAdapter<T> {
public EnumTypeAdapter(Class<T> enumClazz) { ... }
}
public class EnvironmentTypeAdapter EnumTypeAdapter<Environment> {}
public class MonthTypeAdapter extends EnumTypeAdapter<Month> {}
public class RangePropertyType implements TypeAdapter<RangeVO<T extends Comparable<T>>> {
public RangePropertyType(String elementDivider, TypeAdapter<T extends Comparable<T>> type) { ... }
}
public class ListTypeAdapter implements TypeAdapter<List<T>> {
public ListTypeAdapter (String elementDivider, TypeAdapter<T> type, int minElements, int maxElements) { ... }
}
public class ByteSizeTypeAdapter implements TypeAdapter<ByteSizeVO> { // Ex : 3.4 KB / 12.4 MB / 5 GB
public ByteSizePropertyType(ByteSizeVO defaultValue) { ... }
public ByteSizePropertyType(ByteSizeVO minValue, ByteSizeVO maxValue, ByteSizeVO defaultValue) { ... }
}
Value examples
@Value(value = "${my.property}, type = new DirectoryTypeAdapter(true,false))
private File tempDirectory;
@Value(value = "${my.property}, type = new EmailTypeAdapter())
private String adminEmail;
@Value(value = "${my.property}, type = new ListTypeAdapter<String>(new EmailTypeAdapter()))
private List<String> adminEmails;
@Value(value = "${my.property}, type = new ByteSizeTypeAdapter(new ByteSizeVO("4 GB"))
private ByteSizeVO maxFileSize;
@Value(value = "${my.property}, type = new IpRangeTypeAdapter())
private IpRangeVO validIpAddresses;
Comment From: mdeinum
Validation doesn't belong in a conversion framework, but is validation. What you are proposing is already possible with Spring Boot and using JSR-303 for validating properties. A request to move that support from Spring Boot to Spring itself might be viable.
Comment From: snicoll
Thanks for the suggestion but this should move to conversion/validation rather than introducing such type
attribute. There would also be a disconnect when the type
attribute could refer to a type that is incompatible with the type of the field.