Autowiring un servizio in un validatore
-
01-10-2019 - |
Domanda
Questo esempio è un po 'artificiosa; Ho semplificato per rimuovere dettagli estranei e mettere a fuoco il problema che sto avendo. Ho un validatore che assomiglia a questo:
@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 chiamare il validatore dal mio controller in questo modo:
@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);
}
Tuttavia, continuo a ricevere un'eccezione NullPointerException
. Ho aggiunto un punto di interruzione nel mio validatore e ho visto che era usernameService
null
. Perché non è vero sempre autowired? Inizialmente ho pensato che fosse perché non avevo annotato il validatore con @Component
, ma ho ancora lo stesso problema anche dopo l'annotazione di esso. La classe UsernameService
è già stato annotato con @Service
e io in grado di verificare che il suo costruttore viene sempre chiamato.
Sono nuovo di primavera, quindi non sono nemmeno sicuro se è bene per cablare un servizio in un validatore. Che cosa sto facendo di sbagliato?
Soluzione
In primavera, è necessario ottenere ValidatorFactory
(o Validator
stesso) tramite LocalValidatorFactoryBean
invece di Validation.buildDefaultValidatorFactory()
, come descritto nel riferimento .
@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"/>
Modifica Ma forse il modo migliore per farlo è quello di utilizzare la convalida automatica di Spring MVC con @Valid
annotazione:
@RequestMapping
public void checkUsername(Model model, @Valid User user, BindingResult result) {
if (result.hasErrors()) {
...
}
}
Questo richiede anche <mvc:annotation-driven/>
nel config.
Altri suggerimenti
Invece di creare un nuovo validatore, si deve autowire o iniettare al controllore. non viene creato il NPE
dal servizio non essere iniettato come validatore / gestito da primavera.