2012-04-22 105 views
3

親愛的Spring社區,使用JSR-303自定義驗證

我想實現如下:

  • 我想有每個控制器(via @InitBinder
  • 自定義驗證我想春天調用validator.validate()(所以not this way
  • 我想用JSR-303 @Valid標註爲
  • 要驗證的bean(RegistrationForm)沒有任何每個字段的JSR-303註釋
  • 我不想將驗證實現(如Hibernate)包含到classpath中;這將是無用的,從上面的語句

我基本上遵循的步驟中提到here

  • 我添加javax.validation.validation-api:validation-api作爲我的依賴
  • 我用<mvc:annotation-driven />
  • 我記住我的模型@Validpublic String onRegistrationFormSubmitted(@ModelAttribute("registrationForm") @Valid RegistrationForm registrationForm, BindingResult result) ...

所以會發生什麼,是驗證API試圖找到任何實施和失敗:

Caused by: javax.validation.ValidationException: Unable to find a default provider 
    at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:264) 
    at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:183) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417) 

的出路是定義爲AnnotationDrivenBeanDefinitionParser一個validator屬性:

<bean name="validator" class="org.company.module.RegistrationFormValidator" /> 

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

但這種做法意味着驗證將由ConfigurableWebBindingInitializer.initBinder()設置給所有控制器。

我知道我試圖以一種特殊的方式使用框架,但是社區會說什麼,如果validator屬性的特殊含義告訴驗證器不需要解析,例如,

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

經過特殊處理:

--- AnnotationDrivenBeanDefinitionParser.java.orig  2011-06-30 14:33:10.287577300 +0200 
+++ AnnotationDrivenBeanDefinitionParser.java 2011-06-30 14:34:27.897449000 +0200 
@@ -152,6 +152,10 @@ 

     private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) { 
       if (element.hasAttribute("validator")) { 
+      if ("manual".equals(element.getAttribute("validator"))) { 
+        return null; 
+      } 
+ 
         return new RuntimeBeanReference(element.getAttribute("validator")); 
       } 
       else if (jsr303Present) { 

任何反饋意見表示歡迎。

P.S.從Spring Forum轉發。

回答

2

這也是我對above mentioned forum的回答/解決方法的重新發布。無論如何,我認爲這可能有助於在這裏。

我發現的唯一解決方法是實現我自己的@Valid註釋,一旦Spring(至少在3.1.1.RELEASE代碼庫中)只檢查方法參數註釋的簡單名稱(請查看下面的org.springframework.web.method.annotation.ModelAttributeMethodProcessor類)。這樣,我不需要將javax.validation.validation-api:validation-api添加到我的項目的依賴關係中,並且我停止獲取臭名昭着的javax.validation.ValidationException: Unable to find a default provider

/** 
* Validate the model attribute if applicable. 
* <p>The default implementation checks for {@code @javax.validation.Valid}. 
* @param binder the DataBinder to be used 
* @param parameter the method parameter 
*/ 
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { 
    Annotation[] annotations = parameter.getParameterAnnotations(); 
    for (Annotation annot : annotations) { 
     if (annot.annotationType().getSimpleName().startsWith("Valid")) { 
      Object hints = AnnotationUtils.getValue(annot); 
      binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); 
     } 
    } 
} 
+0

謝謝,很好的解決方案。的確,如果'validation-api'不在classpath中,那麼''不會嘗試通過工廠初始化實現。因此將使用特定於binder的驗證器。你是否通過控制器的'initBinder()'設置了驗證器? – 2012-04-23 10:06:52

+0

謝謝!是的,按照文檔中建議的方式使用initBinder()。 – mhnagaoka 2012-04-25 01:39:59

0

您寫道:

我添加javax.validation.validation-API:驗證的API作爲我的依賴......

造成的:javax.validation.ValidationException:無法找到一個默認提供商

您還需要該API的實現。例如Hibernate Validator,它是默認的implmentation。(與ORM休眠無關)

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-validator</artifactId> 
    <version>4.2.0.Final</version> 
</dependency> 
+0

感謝您的回覆。正如我所提到的,我瞭解Hibernate驗證實現。我想要一個沒有它的解決方案。 – 2012-04-23 09:50:14

+0

@dma_k,我明白了,但你爲什麼要重新發明輪子? – Ralph 2012-04-23 10:10:35

+0

我不想重新發明輪子(=驗證引擎)。要驗證的bean('RegistrationForm')沒有任何每個字段的JSR-303註釋,因此真正的實現是無用的。我已經更新了一下這個問題,以清楚地說明這個說法(參見頂部的要點)。正如我所提到的,我知道「直接」使用JSR-303。我提供的補丁(我相信)爲框架增加了更多的靈活性。你怎麼看? – 2012-04-25 07:32:34

1

爲什麼你不想包含Hibernate Validator?如果沒有任何實現(或提供者),那麼以前的JSR規範都有一些實現,您不能使用規範。

你能想象使用JDBC而不使用任何JDBC驅動程序嗎?在沒有提供商的情況下使用JPA?在沒有任何容器的情況下使用Servlet?

它也是一樣的,Hibernate Validator是JSR-303的參考實現,我沒有意識到任何其他的實現,但是如果你不喜歡Hibernate Validator,那就去另一個實現。

+0

我不想包含Hibernate驗證器,因爲它太重了。 Spring框架提供了另一個級別抽象('org.springframework.validation.Validator'),它不綁定到例如JSR-303。我知道如何驗證我的bean,我希望Spring能夠調用這個驗證函數。 Bean'RegistrationForm'沒有任何進一步的JSR-303註釋,所以對於真正的實現是沒有用處的。 – 2012-04-23 09:56:53

+0

還有一點,關於沒有實現的API。有一點不同:使用API​​和使用註釋。接口在沒有實現的情況下是「無用的」,但是註釋不是。真實世界的例子:傑克遜可以在沒有JAXB運行時的情況下使用JAXB註釋。所以'@ Valid'註釋可以用來給Spring發信號以強制驗證;活頁夾將決定,*使用什麼特定的實現*。 – 2012-04-23 10:11:00