2016-07-15 38 views
2

Ich habe ein Spring-MVC-Formular zur Eingabe eines Datums, die Eingabe wird an einen Controller gesendet und über die Standard-Spring-MVC-Validierung validiert.Hinzufügen von ConstraintValidator für @Future und LocalDate zu einem Spring Boot-Projekt

Modell:

public class InvoiceForm { 
    @Future 
    private LocalDate invoicedate; 
} 

Controller:

public String postAdd(@Valid @ModelAttribute InvoiceForm invoiceForm, BindingResult result) { 
    .... 
} 

Wenn Sie das Formular absenden ich die folgende Fehlermeldung erhalten:

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Future' validating type 'java.time.LocalDate'. Check configuration for 'invoicedate' 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorNoUnwrapping(ConstraintTree.java:310) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorInstanceForAutomaticUnwrapping(ConstraintTree.java:244) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getInitializedConstraintValidator(ConstraintTree.java:163) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:116) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:617) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:580) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:524) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:492) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:457) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:407) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:205) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final] 
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:108) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.validation.DataBinder.validate(DataBinder.java:866) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.validateIfApplicable(ModelAttributeMethodProcessor.java:164) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:111) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:99) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at com.balticfinance.jwt.StatelessAuthenticationFilter.doFilter(StatelessAuthenticationFilter.java:46) ~[classes/:na] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176) ~[spring-security-web-4.0.4.RELEASE.jar:4.0.4.RELEASE] 
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE] 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) ~[tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:676) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_91] 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_91] 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.36.jar:8.0.36] 
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_91] 

ich meine eigenen ConstraintValidator für diesen Fall implementiert. Aber irgendwie springt Spring Boot nicht zur Validierung.

@Component 
public class LocalDateFutureValidator implements ConstraintValidator<Future, LocalDate> { 

    @Override 
    public void initialize(Future future) { 
    } 

    @Override 
    public boolean isValid(LocalDate localDate, ConstraintValidatorContext constraintValidatorContext) { 
     LocalDate today = LocalDate.now(); 
     return localDate.isEqual(today) || localDate.isAfter(today); 
    } 
} 

Ich weiß, ich konnte einfach meine eigene Anmerkung schreiben und den Validator dort angeben, ist aber nicht eine saubere und einfache Art und Weise?

+1

Die '@ Component' ist ziemlich nutzlos, da die Validatoren im Frühling nicht verwaltet werden.Ihre '@ Future' sollte angeben, welcher Validator verwendet werden soll. Wenn Sie eine aktuelle Version des Hibernate-Validators verwenden, wird dies standardmäßig unterstützt und Sie benötigen keinen benutzerdefinierten Validator. –

+1

Danke, nach https://hibernate.atlassian.net/browse/HV-874 zu beurteilen @Future mit LocalDate sollte seit Hibernate-Validator 5.2 unterstützt werden. Ich benutze tatsächlich Hibernate-Validator 5.2.4.Final (automatisch von Spring-Boot-Starter-Daten-JPA gezogen, denke ich). Ich benutze Spring-Boot-Starter-Elternteil 1.3.6.RELEASE –

+0

Die Validatoren für LocalDate wurden entfernt, wahrscheinlich sollte ich einen anderen Typ verwenden ?! https://hibernate.atlassian.net/browse/HV-981 –

Antwort

2

Sie werden Ihren eigenen Validator auf die META-INF/validation.xml Datei, wie so hinzufügen müssen:

<constraint-mappings 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd" 
    xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1"> 

    <constraint-definition annotation="javax.validation.constraints.Future"> 
     <validated-by include-existing-validators="true"> 
      <value>package.to.LocalDateFutureValidator</value> 
     </validated-by> 
    </constraint-definition> 
</constraint-mappings> 

Weitere Informationen refer to the official documentation.

+1

Sie müssten die vorhandenen Validatoren nicht ausschließen, aber im Allgemeinen ist die Verwendung von XML zum Hinzufügen des neuen ConstraintValidator das Richtige. Wenn Sie einen nicht standardisierten Ansatz verwenden möchten, können Sie sich auch ConstraintDefinitionContributor ansehen. In diesem Fall können Sie den Java ServiceLoader-Ansatz verwenden, um den neuen Constraint-Validator hinzuzufügen. Trotzdem gibt es einen guten Grund, warum es keine @ Future-Implementierung für LocalDate gibt. Per Definition ist es nicht möglich zu bestimmen, ob ein LocalDate in der Zukunft liegt, da es keine Zeitzoneninformationen enthält. Das könnte in Ihrem Fall in Ordnung sein, aber .. – Hardy

+0

Sie sind nicht ausgeschlossen, aber in der Tat enthalten :) –

+0

Ich habe eine Spring Boot App, die ich versuche, im Grunde dasselbe zu tun. Wie kann ich Spring meinen benutzerdefinierten Validator mitteilen? Ich erhalte die UnexpectedTypeException trotz einer validation.xml-Datei (in meinem Resources-Ordner) –

0

ich mit Miloš zustimmen würde, dass die META-INF/validation.xml Verwendung ist wahrscheinlich die saubersten und einfachsten Weg, aber wenn Sie wirklich wollen es in einer Spring @Confguration Klasse einrichten, dann ist es möglich, und hier ist eine Möglichkeit, wie Sie es tun können.

Das Schöne an Spring Boot ist, dass es für Sie viele Konfigurationsschritte macht, damit Sie sich keine Sorgen machen müssen. Dies kann jedoch auch Probleme verursachen, wenn Sie etwas selbst konfigurieren möchten und es nicht so offensichtlich ist, wie es gemacht wird.

So gestern machte ich mich daran versucht, eine CustomerValidator für @Past und LocalDate mit dem ConstraintDefinitionContributor Mechanismus hinzuzufügen, die Hardy schlägt vor (und ist in der Hibernate Dokumentation bezeichnet).

Die einfache Bit war die implementierende Klasse zu schreiben, die Validierung zu tun, die für meine hochspezifische Zwecke bestand aus:

public class PastValidator implements ConstraintValidator<Past, LocalDate> { 

    @Override 
    public void initialize(Past constraintAnnotation) {} 

    @Override 
    public boolean isValid(LocalDate value, ConstraintValidatorContext context) { 
     return null != value && value.isBefore(LocalDate.now()); 
    } 
} 

Dann bekam ich faul und instanziiert nur @Bean in meiner Konfiguration, nur auf die Es ist unwahrscheinlich, dass eine von Spring's Autokonfigurationsklassen es einfach aufnehmen und in den Hibernate-Validator einbinden würde. Das war ziemlich lang, angesichts der verfügbaren Dokumentation (oder deren Fehlen) und was Hardy und andere gesagt hatten, und es hat sich nicht ausgezahlt.

So feuerte ich einen Debugger und arbeitete nach hinten von der Ausnahme in org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree geworfen, die mir sagt, dass es nicht um eine Prüfung für @Past und LocalDate finden konnte.

bei der Typenhierarchie des ConstraintValidatorFactory Suchen entdeckte ich, dass es in meiner Spring MVC-Anwendung zwei implementierenden Klassen waren: SpringConstraintValidatorFactory und SpringWebConstraintValidatorFactory, die beide nur versuchen, eine Bohne aus dem Kontext der richtigen Klasse zu bekommen.Dies sagte mir, dass ich meinen Validator bei Spring's BeanFactory registrieren lassen musste, aber als ich einen Haltepunkt auf diesen steckte, wurde er nicht für meinen PastValidator getroffen, was bedeutete, dass Hibernate nicht wusste, dass es diesen sogar anfordern sollte Klasse.

Dies ergab einen Sinn: Es gab keine ConstraintDefinitionContributor irgendwo zu tun zu sagen, Hibernate musste Spring für eine Instanz des PastValidator fragen. Das Beispiel in der Dokumentation unter http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#section-constraint-definition-contributor schlägt vor, dass ich Zugriff auf eine HibernateValidatorConfiguration benötigen würde, so dass ich nur herausfinden musste, wo Spring seine Konfiguration machte.

Nach ein wenig Graben fand ich heraus, dass alles in Spring LocalValidatorFactoryBean Klasse, speziell in seiner afterPropertiesSet() Methode passiert. Von seiner javadoc:

/* 
* This is the central class for {@code javax.validation} (JSR-303) setup in a Spring 
* application context: It bootstraps a {@code javax.validation.ValidationFactory} and 
* exposes it through the Spring {@link org.springframework.validation.Validator} interface 
* as well as through the JSR-303 {@link javax.validation.Validator} interface and the 
* {@link javax.validation.ValidatorFactory} interface itself. 
*/ 

Grundsätzlich, wenn Sie nicht eingerichtet haben und konfigurieren Sie Ihre eigene Validator dann ist dies, wo Frühling es für Sie zu tun versucht, und im wahren Frühling Stil bietet es eine praktische Erweiterung Methode, so dass Sie können die Konfiguration vornehmen lassen und dann Ihre eigenen hinzufügen.

So war meine Lösung nur die LocalValidatorFactoryBean so zu erweitern, dass ich in der Lage sein würde meine eigenen ConstraintDefinitionContributor Instanzen zu registrieren:

import java.util.ArrayList; 
import java.util.List; 
import javax.validation.Configuration; 
import org.hibernate.validator.internal.engine.ConfigurationImpl; 
import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; 
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 

public class ConstraintContributingValidatorFactoryBean extends LocalValidatorFactoryBean { 

    private List<ConstraintDefinitionContributor> contributors = new ArrayList<>(); 

    public void addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor) { 
     contributors.add(contributor); 
    } 

    @Override 
    protected void postProcessConfiguration(Configuration<?> configuration) { 
     if(configuration instanceof ConfigurationImpl) { 
      ConfigurationImpl config = ConfigurationImpl.class.cast(configuration); 
      for(ConstraintDefinitionContributor contributor : contributors) 
       config.addConstraintDefinitionContributor(contributor); 
     } 
    } 
} 

und dann instanziiert und diese Konfiguration in meinem Frühling konfigurieren:

@Bean 
    public ConstraintContributingValidatorFactoryBean validatorFactory() { 
     ConstraintContributingValidatorFactoryBean validatorFactory = new ConstraintContributingValidatorFactoryBean(); 
     validatorFactory.addConstraintDefinitionContributor(new ConstraintDefinitionContributor() { 
      @Override 
      public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) { 
        builder.constraint(Past.class) 
          .includeExistingValidators(true) 
          .validatedBy(PastValidator.class); 
      } 
     }); 
     return validatorFactory; 
    } 

und zur Vollständigkeit, hier ist auch, wo ich instanziiert von PastValidator Bean:

@Bean 
    public PastValidator pastValidator() { 
     return new PastValidator(); 
    } 

Andere federnd-thingies

ich in meinem Debuggen bemerkt, dass, weil ich eine recht große Spring MVC-Anwendung haben, ich sah zwei Fälle von SpringConstraintValidatorFactory und einer von SpringWebConstraintValidatorFactory. Ich fand die letzte wurde nie während der Validierung verwendet, so dass ich es vorläufig ignoriert.

Frühling hat auch einen Mechanismus für die Entscheidung, welche die Umsetzung von ValidatorFactory zu verwenden, so ist es möglich, dass es nicht Ihre ConstraintContributingValidatorFactoryBean zu verwenden und stattdessen etwas anderes nutzen (sorry, ich die Klasse gefunden, in dem er das gestern aber couldn‘ Ich finde es heute wieder, obwohl ich nur etwa 2 Minuten damit verbracht habe). Wenn Sie mit Spring MVC in jeder Art von nicht-triviale Weise dann sind die Chancen haben Sie bereits eine eigene Konfigurationsklasse wie diese zu schreiben hatte, die WebMvcConfigurer implementiert, wo man explizit Draht in Ihrem Validator Bohne:

public static class MvcConfigurer implements WebMvcConfigurer { 

    @Autowired 
    private ConstraintContributingValidatorFactoryBean validatorFactory; 

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

    // ... 
    // <snip>lots of other overridden methods</snip> 
    // ... 
} 

Das ist falsch

Wie bereits erwähnt, Sie eine @Past Validierung einer LocalDate der Anwendung sollten vorsichtig sein, weil es Informationen keine Zeitzone ist. Wenn Sie jedoch LocalDate verwenden, weil alles nur in der gleichen Zeitzone läuft oder Sie die Zeitzonen absichtlich ignorieren möchten oder es Ihnen einfach egal ist, dann ist das für Sie in Ordnung.