This example is a bit contrived; I've simplified it to remove extraneous details and to focus on the problem I am having. I have a validator that looks like this:
@Component
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
@Autowired
UsernameService usernameService;
@Override
public void initialize(UniqueUsername uniqueUsername) {
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
return !usernameService.exists(s);
}
}
I call the validator from my controller like this:
@RequestMapping
public void checkUsername(Model model, User user) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> constraintViolations = validator.validateProperty(user, "username");
model.addAttribute("error", constraintViolations.size() > 0);
}
However, I keep getting an NullPointerException
exception. I added a breakpoint in my validator and saw that usernameService
was null
. Why isn't it getting autowired? Initially I thought it was because I hadn't annotated the validator with @Component
, but I still have the same problem even after annotating it. The UsernameService
class has already been annotated with @Service
and I can verify that its constructor is getting called.
I am new to Spring, so I'm not even sure if it is alright to wire a service into a validator. What am I doing wrong?
In Spring, you need to obtain ValidatorFactory
(or Validator
itself) via LocalValidatorFactoryBean
instead of Validation.buildDefaultValidatorFactory()
, as described in the reference.
@Autowired
Validator validator;
@RequestMapping
public void checkUsername(Model model, User user) {
Set<ConstraintViolation<User>> constraintViolations = validator.validateProperty(user, "username");
model.addAttribute("error", constraintViolations.size() > 0);
}
-
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
EDIT: But perhaps the better way to do it is to use Spring MVC's automatic validation with @Valid
annotation:
@RequestMapping
public void checkUsername(Model model, @Valid User user, BindingResult result) {
if (result.hasErrors()) {
...
}
}
This also requires <mvc:annotation-driven/>
in the config.