Situation
I am trying to create a autoconfiguration in which some properties must pass validation only in specific cases. I have a service MyService which requires credentials properties.username and properties.password, and a OncePerRequestFilter, which intercepts controller calls and calls MyService to perform some action, which requires the application's name properties.app-name.
Here is the global layout:
- when I provide no property and no
MyServicebean exists, the autoconfiguration does nothing, - when no
MyServicebean exists, but I provide a username and password in the properties, I will create a newMyServicebean, and I wish to validate the username and password, - when there exists a
MyServicebean, I will create aOncePerRequestFilter, and I wish to validate the application name.
In order to do so, I wanted to use validation groups. I already implemented some validation in configuration properties before, and therefore know that Spring checks them when instantiating the object, but I have never tried with groups.
First try
Code
Autoconfiguration
@AutoConfiguration
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "properties", name = { "username", "password" })
@Validated(CredentialsRequired.class)
public MyService myDefaultService(@Valid MyProperties properties) {
return new MyService(properties);
}
@Bean
@ConditionalOnBean(MyService.class)
@Validated(AppNameRequired.class)
OncePerRequestFilter myFilter(@Valid MyProperties properties, MyService myService) {
return new MyFilter(properties, myService);
}
}
Configuration properties
@Data
@ConfigurationProperties("properties")
public class MyProperties {
@NotBlank(groups = AppNameRequired.class)
private String appName;
@NotBlank(groups = CredentialsRequired.class)
private String username;
@NotBlank(groups = CredentialsRequired.class)
private String password;
}
Result
When I provide no property, nothing fails, which is desired. However, if I just use the following properties:
properties.username=
properties.password=
then both the service and the filter are created without any automatic validation issue, as this short modification shows:
@Bean
@ConditionalOnBean(MyService.class)
@Validated(AppNameRequired.class)
OncePerRequestFilter myFilter(@Valid MyProperties properties, MyService myService, Validator validator) {
if(!validator.validate(properties, AppNameRequired.class).isEmpty()) {
throw new IllegalArgumentException(); // Should not be reached if validation works, but is thrown on instantiation
}
return new MyFilter(properties, myService);
}
Trying with @Validated on properties class
I have also tried applying @Validated on MyProperties, but it did not change much. Furthermore, I noticed this in a more global case:
- when there is
@Validatedon the class, and constraints without groups are all valid, no error is thrown, even if some constraints with groups are invalid - when there is
@Validatedon the class, but at least one constraint without group is violated, Spring fails the instantiation - when there is
@Validated(AppNameRequired.class)on the class, and constraints without groups are all valid, no error is thrown, even if the constraints with groupAppNameRequiredare invalid
My conclusion, and my question
After all of this, my conclusion is that Spring does not support validation groups for bean validation. Hence, I wonder: is there a way to perform this conditional validation by relying on the Jakarta and Spring Boot annotations only, or am I forced to manually call a validator to perform this?