2015-03-07 9 views
9

Angenommen, ich habe eine Probe Entitätsklasse wie folgt aus:Spring MVC - Lookup-Validierer automatisch

public class Address { 
    ... 
} 

und eine entsprechende Validierer:

@Component 
public AddressValidator implements Validator { 

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

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

Wenn ich einen Controller wie die folgenden verwenden, alles funktioniert:

@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) { 
     ... 
    } 
} 

Wenn ich jedoch den Validierer registrieren Teil (dh die folgenden), ist die Validierung nein t durchgeführt.

@Autowired 
private AddressValidator validator; 

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

Das manuelle Registrieren von Validatoren scheint sinnlos. Kann ich Spring anweisen, Validatoren automatisch nachzuschlagen (ähnlich wie Controller nachgeschlagen werden)?

Es ist eine Spring Boot-basierte Anwendung.

+0

Sie sich für eine Art und Weise zu fragen automatisch 'Validator' Kontext Bohnen für die Frühjahrstagung des Validation Framework hinzufügen? – chrylis

+0

Wenn ich eine Bean wie Adressadresse mit einer Annotation @Valid markiere, möchte ich, dass Spring automatisch den richtigen Validierer zwischen Beans sucht, die die Validator-Schnittstelle implementieren. Gibt es im Frühjahr solche Funktionen? Scheint einfach zu implementieren. Auf diese Weise würde ich den richtigen Validator nicht manuell in der initBinder-Methode festlegen müssen. –

+0

Dies ist die Spring 'Validator'-Schnittstelle und nicht' javax.validator.Validator'? Gibt es einen Grund, warum Sie keinen benutzerdefinierten JSR-303-Validator verwenden, vielleicht mit einer benutzerdefinierten '@ ValidAddress'-Annotation? – chrylis

Antwort

1

Sie können den globalen Validator konfigurieren.

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

Wenn Sie mit Java-basierter Federkonfiguration mit WebMvcConfigurationSupport, können Sie außer Kraft setzen getValidator()

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

Sie nennen setValidator (Validator) auf der Welt WebBindingInitializer. Auf diese Weise können Sie eine Validator-Instanz für alle @Controller-Klassen konfigurieren. Dies kann unter Verwendung des Spring MVC-Namensraum leicht erreicht werden:

<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

Sie können nur einen globalen Validierer richtig registrieren? Wie würdest du Spring sagen, dass es mehrere Validatoren lädt und die richtige Implementierung auswählt, wenn ein Parameter mit @Valid markiert ist? – Blacktiger

0

Ich habe einen Build in Spring Lösung für diese nicht gefunden, aber hier ist das, was ich tue.

ich meine Validator Bohnen im Frühjahr Java-Konfiguration erklären, wie so:

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

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

Dann habe ich alle Regler so ein Basis-Controller wie erstreckt:

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)); 
    } 


} 

Die konkrete Klasse dieser Controller bekommt die Liste wird über die Abhängigkeitsinjektion wie folgt injiziert:

@Controller 
public class StudentController extends BaseController<Student>{ 

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

} 

Die bas Der Controller verwendet die @InitBinder-Rückrufmethode, um alle Validatoren zu registrieren.

Ich bin überrascht, dass Spring nicht automatisch alle Objekte im Klassenpfad registriert, die die Validator-Schnittstelle implementieren.

2

Sie können mein Beispiel von gist oder darunter verwenden. Die Idee ist, einen CompositeValidator zu haben, der alle Validator- oder SmartValidator-Instanzen enthält.

Es unterstützt Hinweise und kann auch mit Hibernate Annotation Validator (LocalValidatorFactoryBean) integriert werden. Und es ist auch möglich, mehr als einen Validator pro spezifischem Modell zu haben.


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; 
       } 
      } 
     } 

oder 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>