2015-03-07 18 views
9

Supponiamo che io ho una classe di entità di esempio in questo modo:Spring MVC - ricerca validatori automaticamente

public class Address { 
    ... 
} 

e un corrispondente validatore:

@Component 
public AddressValidator implements Validator { 

    @Override 
    public boolean supports(Class<?> entityClass) { 
     return entityClass.equals(Address.class); 
    } 

    @Override 
    public void validate(Object obj, Errors errors) { 
     ... 
    } 
} 

Quando uso di un controller come il seguente, tutto funziona:

@RestController 
@RequestMapping("/addresses") 
public class AddressController { 

    @Autowired 
    private AddressValidator validator; 

    @InitBinder 
    protected void initBinder(WebDataBinder binder) { 
     binder.setValidator(validator); 
    } 

    @RequestMapping(method=POST) 
    public Long addNewAddress(@Valid @RequestBody Address address) { 
     ... 
    } 
} 

Tuttavia, se ometto la parte di registrazione del validatore (cioè il seguente), la convalida non è t eseguito.

@Autowired 
private AddressValidator validator; 

@InitBinder 
protected void initBinder(WebDataBinder binder) { 
    binder.setValidator(validator); 
} 

La registrazione manuale dei validatori sembra inutile. Posso istruire Spring a cercare automaticamente i validatori (come per il modo in cui vengono controllati i controllori)?

È un'applicazione basata su Spring Boot.

+0

stai chiedendo un modo per aggiungere automaticamente 'fagioli contesto Validator' al quadro di convalida primavera? – chrylis

+0

Quando contrassegno un bean come Indirizzo indirizzo con un'annotazione @Valid, vorrei che Spring verificasse automaticamente il validatore corretto tra i bean che implementano l'interfaccia del Validator. C'è una tale funzionalità in primavera? Sembra facile da implementare. In questo modo non avrei bisogno di impostare manualmente il validatore corretto nel metodo initBinder. –

+0

Questa è l'interfaccia Spring 'Validator' e non' javax.validator.Validator'? C'è un motivo per cui non stai utilizzando un validatore JSR-303 personalizzato, magari con un'annotazione personalizzata '@ ValidAddress'? – chrylis

risposta

1

È possibile configurare il Validatore globale.

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#validation-mvc

Se si sta utilizzando la configurazione di primavera basato su Java con WebMvcConfigurationSupport, è possibile ignorare getValidator()

/** 
* Override this method to provide a custom {@link Validator}. 
*/ 
protected Validator getValidator() { 
    return null; 
} 

si può chiamare setValidator (Validator) sul WebBindingInitializer globale. Ciò consente di configurare un'istanza Validator su tutte le classi @Controller. Ciò può essere ottenuto facilmente utilizzando lo spazio dei nomi Spring MVC:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd 
     http://www.springframework.org/schema/mvc 
     http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 

    <mvc:annotation-driven validator="globalValidator"/> 

</beans> 
+2

È possibile registrare solo un validatore globale giusto? Come diresti a primavera di caricare più validatori e scegliere l'implementazione corretta da utilizzare quando un parametro è contrassegnato con @Valid? – Blacktiger

0

non ho trovato un accumulo in soluzione primavera per questo, ma qui è quello che faccio.

dichiaro i miei fagioli validatore nella configurazione di Java primavera in questo modo:

@Bean 
public Validator studentValidator(){ 
    return new StudentValidator(); 
} 

@Bean 
public Validator carValidator(){ 
    return new CarValidator(); 
} 

Poi ho tutti i controller si estende un controller di base in questo modo:

public abstract class BaseController <T> { 
    public BaseController(List<Validator> validators) { 
     super(); 
     this.validators = validators; 
     } 
    //Register all validators 
    @InitBinder 
    protected void initBinder(WebDataBinder binder) { 
    validators.stream().forEach(v->binder.addValidators(v)); 
    } 


} 

La classe concreta di questo controller ottiene l'elenco iniettato tramite dipendenza dipendenza, in questo modo:

@Controller 
public class StudentController extends BaseController<Student>{ 

    @Inject 
     public StudentController(List<Validator> validators) { 
     super(validators); 
     } 

} 

Il bas e Controller usa il metodo di richiamo di @InitBinder per registrare tutti i Validator.

Sono sorpreso che primavera non registri automaticamente tutti gli oggetti nel percorso di classe che implementano l'interfaccia del Validatore.

2

È possibile utilizzare il mio esempio da gist o inferiore. L'idea è di avere un principale CompositeValidator che sarà il titolare di tutte le istanze di Validator o SmartValidator.

Supporta i suggerimenti e può anche essere integrato con Hibernate Annotation Validator (LocalValidatorFactoryBean). E inoltre è possibile avere più di un validatore per modello specifico.


CompositeValidator.java

@Component 
public class CompositeValidator implements SmartValidator { 
    @Autowired 
    private List<Validator> validators = Collections.emptyList(); 

    @PostConstruct 
    public void init() { 
     Collections.sort(validators, AnnotationAwareOrderComparator.INSTANCE); 
    } 

    @Override 
    public boolean supports(Class<?> clazz) { 
     for (Validator validator : validators) { 
      if (validator.supports(clazz)) { 
       return true; 
      } 
     } 
     return false; 
    } 

    @Override 
    public void validate(Object target, Errors errors) { 
     validate(target, errors, javax.validation.groups.Default.class); 
    } 

    @Override 
    public void validate(Object target, Errors errors, Object... validationHints) { 
     Class<?> clazz = target.getClass(); 

     for (Validator validator : validators) { 
      if (validator.supports(clazz)) { 
       if (validator instanceof SmartValidator) { 
        ((SmartValidator) validator).validate(target, errors, validationHints); 
       } else { 
        validator.validate(target, errors); 
       } 
      } 
     } 
    } 
} 

SomeController.java

@Controller 
@RequestMapping("/my/resources") 
public class SomeController { 

    @RequestMapping(method = RequestMethod.POST) 
    public Object save(
      @Validated(javax.validation.groups.Default.class) // this interface descriptor (class) is used by default 
      @RequestBody MyResource myResource 
    ) { return null; } 
} 

Java Config

@Configuration 
    public class WebConfig { 
     /** used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy */ 
     // @Bean 
     // public Validator jsr303Validator() { 
     // LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); 
     // // validator.setValidationMessageSource(...); 
     // return validator; 
     // } 

     @Bean 
     public WebMvcConfigurerAdapter webMvcConfigurerAdapter() { 
      return new WebMvcConfigurerAdapter() { 
       @Autowired 
       private CompositeValidator validator; 

       @Override 
       public Validator getValidator() { 
        return validator; 
       } 
      } 
     } 

o XML Config

<!-- used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy --> 
<!--<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">--> 
<!-- <property name="validationMessageSource" ref="messageSource"/>--> 
<!--</bean>--> 

<mvc:annotation-driven validator="compositeValidator"> 
    //... 
</mvc:annotation-driven>