I am working on an app using spring boot for the backend and vaadin for the frontend. I need to add validation, which needs to do a database check - is email registered in this particular example.
@Component
public class EmailExistsValidator implements ConstraintValidator<EmailExists, CharSequence> {
private final UserRepo userRepo;
@Autowired
public EmailExistsValidator(UserRepo userRepo) {
this.userRepo = userRepo;
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
//check email does not exist logic here
}
}
I have successfuly used this setup in spring mvc and spring rest applications, with no additional configurations. Unfortunately, the above is not working with vaadin. After some debugging I found out that spring indeed creates and manages those components, but they are not the ones being used. Instead vaadin creates and manages other instances of ConstraintValidator, when the actual validation is happening. The validation is done with Binder.writeBeanIfValid(), if that matters.
I went through:
WebApplicationContext
in order to use AutowireCapableBeanFactory.autowireBean()
to autowire the annotated fields. Unsurprisingly, the context was null
when vaadin creates/manages the instance, so it did not work.@Component
public class EmailExistsValidator implements ConstraintValidator<EmailExists, CharSequence> {
private static UserRepo repo;
private final UserRepo userRepo;
public EmailExistsValidator() {
this.userRepo = repo;
}
@Bean
public static UserRepo setRepo(UserRepo userRepo) {
repo = userRepo;
return repo;
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
//validation logic
}
}
This approach is based on this answer (from the second question I linked). It does the job (only this worked for me), but it's way too hacky for my tastes.
How can I configure vaadin to use spring managed ConstraintValidator
s, instead of vaadin managed ones? Or how can I autowire spring components in ConstraintValidator
s created and managed by vaadin?
There is often a situation where auto wiring is not working because Spring does not manage the component.
For that purpose, you can create a class that will hold the ApplicationContext like this:
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getBean(Class<T> type) {
return applicationContext.getBean(type);
}
@Override
public void setApplicationContext(@SuppressWarnings("NullableProblems") ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = applicationContext;
}
}
Now you can use this class to get a reference to a bean using the static method:
UserRepo userRepo = ApplicationContextHolder.getBean(UserRepo.class);