Spring externalized configuration can be validated using JSR 303 Bean Validation API. But it requires Spring Type-safe Configuration Properties instead of @Value("${property}")
.
Add Hibernate Validator dependency to build.gradle
implementation 'org.hibernate.validator:hibernate-validator'
The type-safe configuration properties must be annotated with @Validated
and the field someJsonString
with a custom annotation @ValidJsonConstraint
@Component
@ConfigurationProperties("source")
@Validated
public class SourceProperties {
@ValidJsonConstraint
private String someJsonString;
public String getSomeJsonString() {
return someJsonString;
}
public void setSomeJsonString(String someJsonString) {
this.someJsonString = someJsonString;
}
}
You can inject the properties into all required services, so the validation code is not duplicated
@Autowired
private SourceProperties sourceProperties;
It's time to create the custom annotation
@Documented
@Constraint(validatedBy = ValidJsonValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidJsonConstraint {
String message() default "Invalid JSON";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and a validator to validate fields annotated with the custom annotation
public class ValidJsonValidator implements ConstraintValidator<ValidJsonConstraint, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void initialize(ValidJsonConstraint constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
try {
objectMapper.readTree(value);
return true;
} catch (JsonProcessingException e) {
return false;
}
}
}
When in the application.properties the source.someJsonString
value is valid JSON
source.someJsonString={"test":"qwe"}
application successfully starts.
When JSON is invalid
source.someJsonString=qwe
Application fails to start with the following exception
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'source' to intellectsoft.afgruppen.shiftschedule.SourceProperties failed:
Property: source.someJsonString
Value: qwe
Origin: class path resource [application.properties]:26:23
Reason: Invalid JSON
Action:
Update your application's configuration
Also, the same can be achieved a bit easier without JSR 303 Bean Validation API.
Create a custom validation component
@Component
public class JsonValidator {
private final ObjectMapper objectMapper = new ObjectMapper();
public boolean isValid(String value) {
try {
objectMapper.readTree(value);
return true;
} catch (JsonProcessingException e) {
return false;
}
}
}
Inject the validator and perform validation in the property setter
@Component
@ConfigurationProperties("source")
public class SourceProperties {
private final JsonValidator jsonValidator;
private String someJsonString;
public SourceProperties(JsonValidator jsonValidator) {
this.jsonValidator = jsonValidator;
}
public String getSomeJsonString() {
return someJsonString;
}
public void setSomeJsonString(String someJsonString) {
if (!jsonValidator.isValid(someJsonString)) {
throw new IllegalArgumentException(someJsonString + " is not a valid JSON");
}
this.someJsonString = someJsonString;
}
}