As seen in this Baeldung example, it is possible to write plain old javax.validation
objects and Spring is able to manage them. For example, it is possible to perform field injection in a validator without annotating it as a component:
public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
@Autowired
private MyBean bean;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return bean.getValidElements().contains(value);
}
}
Yet, MyValidator
is not a Spring bean: a beanFactory.getBean(MyValidator.class)
(or an applicationContext.getBean(MyValidator.class)
) will throw
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'my.package.MyValidator' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
This can be fixed adding a @Component
annotation on MyValidator
, but maybe it is cleaner to not annotate them as components: I have several validators that extends a common abstract class, so I'm planning to annotate that class in this way, but I feel it would be more maintenable to just have a beanFactory.getBean(MyValidator.class)
of sorts and keep each validator as plain as possible.
Is there a way to retrieve the validators without annotating them as components, as Spring already manage them somehow? A minimal example can be found here.
The Validation API has support to have dependencies injected into the ConstraintValidator
based on the annotation metadata. This has nothing to do with Spring but is supported by the Validation API.
To support the injection there is the [javax.jakarta].validation.ConstraintValidatorFactory
interface. Spring has 2 implementations for this, but commonly used is the SpringWebConstraintValidatorFactory
.
What happens is that when validation is requested based on the annotations present the Validation API will call ConstraintValidatorFactory.create
to get an instance of the needed validator(s).
Spring will construct an instance on the fly and if there are @Autowired
or @Inject
fields (or constructors), Spring will happily inject the dependencies.
The created validators are NOT Spring Managed beans. The lifecycle of those validators is totally in control of the Validation API and not Spring. As such they are also not retrievable from the ApplicationContext
, unless you explictly make them beans.
To make them beans you can do 1 of several things.
@Component
.@Configuration
class with @Bean
methods@ComponentScan(includeFilter = @Filter(type=ASSIGNABLE_TYPE, classes = ConstraintValidator.class)
.Either will (or can) result in your validators being Spring managed.