Hibernate Validator

摘要:

在項目的業務屬性中,你是否是要常常驗證屬性的取值範圍呢. 想要了解比較優美的解決方案嗎? 看看Hibernate Validator 是怎麼作的吧.一見到她,相信你就會說: Oh God, 這就是我須要的.

做者:icess(做者的blog: http://blog.matrix.org.cn/page/icess)
關鍵字:Hibernate Validator
              
用Annotations 給類或者類的屬性加上約束(constraint),在運行期檢查屬性值是很優雅的.Hibernate Validator就是這樣的一個框架.該框架是十分容易的(就像參考文檔中宣稱的那樣),幾乎沒有什麼學習曲線,Validator 是一個驗證框架 不須要和Hibernate的其餘部分綁定就可使用,只要在你的項目中添加Hibernate-annotations.jar庫就能夠了.那麼下面就 讓咱們看看怎麼使用吧.

Person.java 類
/*
 * Created on 2006-1-12 Person.java
 * @author 
 */
package test.annotation.validator;

import org.hibernate.validator.Length;
import org.hibernate.validator.Min;
import org.hibernate.validator.Valid;
 

//@Serializability  //測試自定義約束
public class Person {

  private String name;
  private int age;
  private Address address;
  
  public Person() {}
  
  @Valid //注意此處
  public Address getAddress() {
    return address;
  }
  public void setAddress(Address address) {
    this.address = address;
  }
  
  @Min(value = 1)
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  
  @Length(min = 4)
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
} 

Address.java 類html

/*
 * Created on 2006-1-12 Address.java
 * @author 
 */
package test.annotation.validator;

import org.hibernate.validator.Length;
import org.hibernate.validator.Max;
import org.hibernate.validator.Min;

public class Address {

  private String street;
  private int num;
  
  public Address() {}
  
  @Min(value = 1)
  @Max(value = 100)
  public int getNum() {
    return num;
  }
  public void setNum(int num) {
    this.num = num;
  }
  
  @Length(min = 3,max = 8)
  public String getStreet() {
    return street;
  }
  public void setStreet(String street) {
    this.street = street;
  }
} 
上面是兩個用 Validator Annotations 註釋的 類. 每一個屬性都用 約束限制了.  下面看看測試的類吧:


TestValidator.java 類java

/*
 * Created on 2006-1-12
 * @author icerain
 */
package test.annotation.validator;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;


public class TestValidator {
  public void test() {
    Address add = new Address();
    add.setNum(0);
    add.setStreet("1");
    
    Person p = new Person();
    p.setAddress(add);
    p.setAge(0);
    p.setName("ice");
    
    /******************Test validator ********/
    // 注意該處只驗證了Person 爲了說明 @Valid 註釋的使用
    ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class);
    InvalidValue[] validMessages = classValidator.getInvalidValues(p);
    for (InvalidValue value : validMessages) {
      
    System.out.println("InvalidValue 的長度是:" + validMessages.length
        +" . 驗證消息是: " + value.getMessage() 
        + " . PropertyPath 是:" + value.getPropertyPath()
        +" .\n\t PropertyName 是: " +value.getPropertyName()
        + "Value 是: " + value.getValue()
        +" Bean 是: "+ value.getBean()
        +"\n\t BeanClass 是:" + value.getBeanClass());
    }
  }
  
  public static void main(String[] args) {
    new TestValidator().test();
  }
}
程序的輸出以下 
InvalidValue 的長度是:4 . 驗證消息是: 必須大於等於 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@dd87b2

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的長度是:4 . 驗證消息是: 長度必須介於 4 與 2147483647 之間 . PropertyPath 是:name .

PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@dd87b2

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的長度是:4 . 驗證消息是: 必須大於等於 1 . PropertyPath 是:address.num .

PropertyName 是: num. Value 是: 0 Bean 是: test.annotation.validator.Address@197d257

BeanClass 是:class test.annotation.validator.Address

InvalidValue 的長度是:4 . 驗證消息是: 長度必須介於 3 與 8 之間 . PropertyPath 是:address.street .

PropertyName 是: street. Value 是: 1 Bean 是: test.annotation.validator.Address@197d257

BeanClass 是:class test.annotation.validator.Address


能夠看出不知足約束的值都被指出了.

同時該句: ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class);

咱們只驗證了 Person. 在Person裏面的Address的屬性 因爲有@Valid Annotations 因此 Address的相關屬性也被機聯驗證了 .



若是 把@Valid Annotations 去掉,結果以下:

InvalidValue 的長度是:2 . 驗證消息是: 必須大於等於 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@18fef3d

BeanClass 是:class test.annotation.validator.Person

InvalidValue 的長度是:2 . 驗證消息是: 長度必須介於 4 與 2147483647 之間 . PropertyPath 是:name .

PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@18fef3d

BeanClass 是:class test.annotation.validator.Person

能夠看出 沒有驗證 Address.



固然了 ,你還能夠只驗證一個屬性 , 沒有必要驗證整個類.只須要在調用classValidator.getInvalidValues(p,"age")方法時 加上你要驗證的屬性就能夠了.如咱們只想驗證age 屬性 把代碼改成以下所示:

InvalidValue[] validMessages = classValidator.getInvalidValues(p,"age"); //只驗證age 屬性

運行結果以下:

InvalidValue 的長度是:1 . 驗證消息是: 必須大於等於 1 . PropertyPath 是:age .

PropertyName 是: age. Value 是: 0 Bean 是: test.annotation.validator.Person@1457cb

BeanClass 是:class test.annotation.validator.Person

只是驗證了 age 屬性.

怎麼樣 ,很簡單吧. 關於 Hibernate Validator 內建的驗證Annotations 你們能夠看看 API 或者 參考文檔(中文版我正在翻譯中 請訪問個人 Blog 得到最新信息).git

自定義約束:

若是你要寫本身的約束呢 , 你不用擔憂 ,這也是很容易的. 正則表達式

任何約束有兩部分組成: [約束描述符 即註釋]the constraint descriptor (the annotation) 和[約束validator 即 實現類] the constraint validator (the implementation class).express

下面咱們擴展Hibernate Test suit 中的一個Test 來說解一下.

首先: 要聲明一個constraint descriptor .以下:

設計模式

package test.annotation.validator;

import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;

import org.hibernate.validator.ValidatorClass;

/**
 * Dummy sample of a bean-level validation annotation
 *
 * @author Emmanuel Bernard
 */
@ValidatorClass(SerializabilityValidator.class)
@Target({METHOD,FIELD,TYPE})
@Retention(RUNTIME)
@Documented
public @interface Serializability {
  int num() default 11; 
  String message() default "bean must be serialiable";
} 
@ValidatorClass(SerializabilityValidator.class) 指出了 constraint validator 類.

@Target({METHOD,FIELD,TYPE})
@Retention(RUNTIME)
@Documented

這幾個我就不用解釋了吧.

Serializability 裏面聲明瞭一個 message 顯示約束的提示信息. num 只是爲了說明一個方面 在這裏面沒有實際用途用 .

而後就是 實現一個constraint validator 類 該類要實現Validator<ConstraintAnnotation>.這裏是SerializabilityValidator.java 以下:api

//$Id: SerializabilityValidator.java,v 1.3 2005/11/17 18:12:11 epbernard Exp $
package test.annotation.validator;

import java.io.Serializable;

import org.hibernate.validator.Validator;

/**
 * Sample of a bean-level validator
 *
 * @author Emmanuel Bernard
 */
public class SerializabilityValidator implements Validator<Serializability>, Serializable {
  public boolean isValid(Object value) {
   //這裏只是Validator 裏面的 實現驗證規則的 方法. value 是要驗證的值.
    System.out.println("IN SerializabilityValidator isValid:"+value.getClass()+": " +value.toString()); 
    return value instanceof Serializable;
  }

  public void initialize(Serializability parameters) {
    // 在這裏能夠 取得 constraint descriptor 裏面的屬性 如上面咱們聲明的 num 
    System.out.println("IN SerializabilityValidator: parameters:"+ parameters.num() );
  }
} 

而後在你的類中應用@Serializability  就能夠約束一個類實現Serializable 接口了. 以下:數組

在咱們的Person.java類 添加@Serializability  Annotations ,把Person.java 中的 //@Serializability //測試自定義約束 註釋去掉就ok了.

運行結果以下:

InvalidValue 的長度是:3 . 驗證消息是: bean must be serialiable . PropertyPath 是:null .

PropertyName 是: null. Value 是: test.annotation.validator.Person@1a73d3c Bean 是: test.annotation.validator.Person@1a73d3c

BeanClass 是:class test.annotation.validator.Person

如今把Person類實現 java.io.Serializable 接口 則沒有出現 驗證錯誤消息.

消息的國際化也是很簡單的,把Serializability  中的message 改成以{}擴住的 屬性文件的Key就能夠了

session

public @interface Serializability {
  int num() default 11; 
  String message() default "{Serializable}"; //"bean must be serialiable"; //消息的國際化
}

然 後編輯資料文件. 注意 該資源文件中要包括 Hibernate Validator 內建的資源. 能夠在該org\hibernate\validator\resources 包裏面的資源文件基礎上修改 ,在打包裏面 這樣就能夠了. 本身打包可能不太方便.你能夠把該包裏面的文件複製出來.而後放到你本身的項目包下在本身編輯, 該測試中 我是放在 test\resources 包下的.

而後在 資源文件中添加 Serializable = '''''' 這麼一行, 樣例以下:

#DefaultValidatorMessages.properties (DefaultValidatorMessages_zh.properties 再也不列出^_^)


#下面是 Hibernate Validator 內建的國際化消息

validator.assertFalse=assertion failed

validator.assertTrue=assertion failed

validator.future=must be a future date

validator.length=length must be between {min} and {max}

validator.max=must be less than or equal to {value}

validator.min=must be greater than or equal to {value}

validator.notNull=may not be null

validator.past=must be a past date

validator.pattern=must match "{regex}"

validator.range=must be between {min} and {max}

validator.size=size must be between {min} and {max}

#下面是自定義的消息

Serializable=Bean not Serializable  //加上本身定義的國際化消息.

在構造ClassValidator 時要添上 資源文件 以下:(在測試類中)

ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class,ResourceBundle.getBundle("test.resources.DefaultValidatorMessages"));// 加載資源

這樣就能夠了 .  固然 你還能夠 更改 Hibernate Validator 的消息(不是在上面的資源文件中直接修改validator.length = ... 等等 ) , 還記得 Validator 註釋中有個 message 元素嗎? 你之前用的都是默認值,如今你能夠該爲你本身定義的了.如:validator.length 我把他改成 "該字符串的長度不符合規定範圍範圍". 在資源文件中添加一行鍵值屬性對(key定義爲 "myMsg")以下:

myMsg=該字符串的長度不符合規定範圍範圍

而且還要在@Length 註釋中提供message的引用的key 以下@Length(min = 4,message = "{myMsg}")

再一次運行測試 ,咱們就能夠看到上面兩條自定義綁定的消息了 .以下:

InvalidValue 的長度是:3 . 驗證消息是: Bean 不是 可 Serializable . PropertyPath 是:null .
PropertyName 是: null. Value 是: test.annotation.validator.Person@1bd4722 Bean 是: test.annotation.validator.Person@1bd4722
BeanClass 是:class test.annotation.validator.Person


InvalidValue 的長度是:3 . 驗證消息是: 該字符串的長度不符合規定範圍範圍 . PropertyPath 是:name .
PropertyName 是: name. Value 是: ice Bean 是: test.annotation.validator.Person@1bd4722
BeanClass 是:class test.annotation.validator.Person

怎麼樣,比你想象的簡單吧.

OK 上面咱們討論了 Hibernate Validator 的主要用法: 可是 該框架有什麼用呢? ^_^app

內置的驗證約束註解

內置的驗證約束註解以下表所示(摘自hibernate validator reference):

驗證註解

驗證的數據類型

說明

@AssertFalse

Boolean,boolean

驗證註解的元素值是false

@AssertTrue

Boolean,boolean

驗證註解的元素值是true

@NotNull

任意類型

驗證註解的元素值不是null

@Null

任意類型

驗證註解的元素值是null

@Min(value=值)

BigDecimal,BigInteger, byte,

short, int, long,等任何Number或CharSequence(存儲的是數字)子類型

驗證註解的元素值大於等於@Min指定的value值

@Max(value=值)

和@Min要求同樣

驗證註解的元素值小於等於@Max指定的value值

@DecimalMin(value=值)

和@Min要求同樣

驗證註解的元素值大於等於@ DecimalMin指定的value值

@DecimalMax(value=值)

和@Min要求同樣

驗證註解的元素值小於等於@ DecimalMax指定的value值

@Digits(integer=整數位數, fraction=小數位數)

和@Min要求同樣

驗證註解的元素值的整數位數和小數位數上限

@Size(min=下限, max=上限)

字符串、Collection、Map、數組等

驗證註解的元素值的在min和max(包含)指定區間以內,如字符長度、集合大小

@Past

java.util.Date,

java.util.Calendar;

Joda Time類庫的日期類型

驗證註解的元素值(日期類型)比當前時間早

@Future

與@Past要求同樣

驗證註解的元素值(日期類型)比當前時間晚

@NotBlank

CharSequence子類型

驗證註解的元素值不爲空(不爲null、去除首位空格後長度爲0),不一樣於@NotEmpty,@NotBlank只應用於字符串且在比較時會去除字符串的首位空格

@Length(min=下限, max=上限)

CharSequence子類型

驗證註解的元素值長度在min和max區間內

@NotEmpty

CharSequence子類型、Collection、Map、數組

驗證註解的元素值不爲null且不爲空(字符串長度不爲0、集合大小不爲0)

@Range(min=最小值, max=最大值)

BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子類型和包裝類型

驗證註解的元素值在最小值和最大值之間

@Email(regexp=正則表達式,

flag=標誌的模式)

CharSequence子類型(如String)

驗證註解的元素值是Email,也能夠經過regexp和flag指定自定義的email格式

@Pattern(regexp=正則表達式,

flag=標誌的模式)

String,任何CharSequence的子類型

驗證註解的元素值與指定的正則表達式匹配

@Valid

任何非原子類型

指定遞歸驗證關聯的對象;

如用戶對象中有個地址對象屬性,若是想在驗證用戶對象時一塊兒驗證地址對象的話,在地址對象上加@Valid註解便可級聯驗證

 

此處只列出Hibernate Validator提供的大部分驗證約束註解,請參考hibernate validator官方文檔瞭解其餘驗證約束註解和進行自定義的驗證約束註解定義。



看到這裏其實不用我在多說了 你們都知道怎麼用,何時用. 做爲一篇介紹性文章我仍是在此給出一個最經常使用的例子吧,更好的使用方式你們慢慢挖掘吧.

比 如 : 你如今在開發一我的力資源(HR)系統 (實際上是咱們ERP課程的一個做業 ^_^), 裏面要處理大量的數據,尤爲是在輸入各類資料時 如 登記員工信息. 若是你公司的員工的年齡要求是18 -- 60 那麼你所輸入的年齡就不能超出這個範圍. 你可能會說這很容易啊 , 不用Validator就能夠解決啊.這保持數據前驗證就能夠啦 如if ( e.getAge() > 60 || e.getAge() < 18 ) ........ 給出錯誤信息 而後提示從新輸入不就OK啦 用得着 興師動衆的來個第三方框架嗎?

是啊 當就驗證這一個屬性時, 沒有必要啊 ! 可是一個真正的HR 系統,會只有一個屬性要驗證嗎? 恐怕要有N多吧

你要是每個都那樣 寫一段驗證代碼 是否是很煩啊 ,何況也不方便代碼重用. 如今考慮一些 Validator 是否是更高效啊,攔截到 約束違例的 屬性 就能夠直接獲得 國際化的消息 能夠把該消息顯示到一個彈出對話框上 提示更正  !

Validator的用處不僅這一種 ,你能夠想到如何用呢 ! 歡迎發表你的高見!!

OK 到此 咱們的 Hibernate Validator 之旅就要先告一段落了 . 但願這是令你心曠神怡的一次寒冬之旅,

把你學到的應用到你的項目中吧,必定會提升你的生產率的. 相信我 ,沒錯的  ^_^ !


解析 Hibernate Validator

icess 發表於 2006-01-19
點擊數:427 評論數:0 評價:5/1
關鍵詞:Hibernate validator

摘要:

Hibernate Validator 能夠是一個獨立的驗證框架, 因此看完這篇分析 你能夠把她獨立出來做爲你的我的驗證框架來使用了 ^_^(若是你有興趣和時間的話). Hibernate Validator 框架裏面有兩個主要的類: ClassValidator 和InvalidValue 還有一個接口Validator,在這三個主要的構件?最主要的就只有一個 那就是ClassValidator.另外兩個是很好理解的..

文章工具

在前一篇文章 < Hibernate Validator 簡介 > http://www.matrix.org.cn/resource/article/44/44153_Hibernate%20Validator%20.html中,咱們看到了Hibernate Validator的使用方法,和自定義驗證Annotation的實現以及錯誤消息的國際化等常見問題.

任何得到Matrix受權的網站,轉載請保留如下做者信息和連接:
做者:icess(做者的blog:http://blog.matrix.org.cn/page/icess)
關鍵字:Hibernate Validator

在使用如此優雅的屬性驗證框架的同時,你是否想了解她的細節呢?她到底是怎麼實現的呢? 那麼如今就跟隨我來探探她的內核吧!

Hibernate Validator 能夠是一個獨立的驗證框架, 因此看完這篇分析 你能夠把她獨立出來做爲你的我的驗證框架來使用了 ^_^(若是你有興趣和時間的話). Hibernate Validator 框架裏面有兩個主要的類: ClassValidator 和InvalidValue 還有一個接口Validator,在這三個主要的構件中 最主要的就只有一個 那就是ClassValidator.另外兩個是很好理解的..

如今就讓咱們開始吧. 遵循由淺入深的習慣 咱們先看看 Validator 接口吧. 其代碼以下:

import java.lang.annotation.Annotation;

/**
* A constraint validator for a particular annotation
*
* @author Gavin King
*/
public interface Validator<A extends Annotation> {
  /**
   * does the object/element pass the constraints
   */
  public boolean isValid(Object value);

  /**
   * Take the annotations values
   * @param parameters
   */
  public void initialize(A parameters);
}



Validator接口就是咱們自定義約束的實現類要繼承的接口,該接口在< Hibernate Validator 簡介 > http://www.matrix.org.cn/resource/article/44/44153_Hibernate%20Validator%20.html中已經討論過了,請參考.

InvalidValue 類 你們看名字就應該能夠猜到她的做用了吧. 她就是表明一個沒有經過驗證的錯誤實例.該類定義了一些方法,經過這些方法你能夠取得與該Validator Annotation 有關的一些參數,如:她所註釋的屬性的值,錯誤消息等等. 該類的源代碼以下:

import java.io.Serializable;

/**
* A single violation of a class level or method level constraint.
*
* @author Gavin King
*/
public class InvalidValue implements Serializable {
  private final String message;
  private final Object value;
  private final String propertyName;
  private final Class beanClass;
  private final Object bean;
  private Object rootBean;

  public Object getRootBean() {
    return rootBean;
  }

  public String getPropertyPath() {
    return propertyPath;
  }

  private String propertyPath;

  public InvalidValue(String message, Class beanClass, String propertyName, Object value, Object bean) {
    this.message = message;
    this.value = value;
    this.beanClass = beanClass;
    this.propertyName = propertyName;
    this.bean = bean;
    this.rootBean = bean;
    this.propertyPath = propertyName;
  }

  public void addParentBean(Object parentBean, String propertyName) {
    this.rootBean = parentBean;
    this.propertyPath = propertyName + "." + this.propertyPath;
  }

  public Class getBeanClass() {
    return beanClass;
  }

  public String getMessage() {
    return message;
  }

  public String getPropertyName() {
    return propertyName;
  }

  public Object getValue() {
    return value;
  }

  public Object getBean() {
    return bean;
  }

  public String toString() {
    return propertyName + ' ' + message;
  }

}



而後,就讓咱們看看最主要的類吧:ClassValidator . 該類代碼有400餘行,我都作了詳細的註釋以下:

import 該部分省略了;


/**
* Engine that take a bean and check every expressed annotation restrictions
*
* @author Gavin King
*/
public class ClassValidator<T> implements Serializable {
  private static Log log = LogFactory.getLog( ClassValidator.class );
  private static final InvalidValue[] EMPTY_INVALID_VALUE_ARRAY = new InvalidValue[]{};
  private final Class<T> beanClass;
  private transient ResourceBundle messageBundle;
  private transient boolean defaultResourceBundle;

  private final transient Map<Class, ClassValidator> childClassValidators;
  private transient List<Validator> beanValidators;
  private transient List<Validator> memberValidators;
  private transient List<Member> memberGetters;
  private transient Map<Validator, String> messages;
  private transient List<Member> childGetters;
  private static final String DEFAULT_VALIDATOR_MESSAGE = "org.hibernate.validator.resources.DefaultValidatorMessages";


  /**
   * create the validator engine for this bean type
   */
  public ClassValidator(Class<T> beanClass) {
    this( beanClass, null );
  }

  /**
   * create the validator engine for a particular bean class, using a resource bundle
   * for message rendering on violation
   */
  public ClassValidator(Class<T> beanClass, ResourceBundle resourceBundle) {
    this( beanClass, resourceBundle, new HashMap<Class, ClassValidator>() );
  }

  protected ClassValidator(
      Class<T> beanClass, ResourceBundle resourceBundle, Map<Class, ClassValidator> childClassValidators
  ) {
    this.beanClass = beanClass;
    this.messageBundle = resourceBundle == null ?
        getDefaultResourceBundle() :
        resourceBundle;
    this.childClassValidators = childClassValidators;
    initValidator( beanClass, childClassValidators, this.messageBundle );  //重要的是該初始化函數
  }

  private ResourceBundle getDefaultResourceBundle() {
    ResourceBundle rb;
    try {
      rb = ResourceBundle.getBundle( "ValidatorMessages" );
    }
    catch( MissingResourceException e) {
      //the user did not override the default ValidatorMessages
      log.debug( "ResourceBundle ValidatorMessages not found. Delegate to " + DEFAULT_VALIDATOR_MESSAGE);
      rb = ResourceBundle.getBundle( DEFAULT_VALIDATOR_MESSAGE );
    }
    defaultResourceBundle = true;
    return rb;
  }

  private void initValidator(
      Class<T> beanClass, Map<Class, ClassValidator> childClassValidators,
      ResourceBundle resourceBundle
  ) {
    beanValidators = new ArrayList<Validator>(); // 保存類級別的驗證約束實現類
    memberValidators = new ArrayList<Validator>(); // 保存方法級別的驗證約束實現類
    memberGetters = new ArrayList<Member>();// 保存類的成員(字段or方法)和構造函數方法的標識信息
    messages = new HashMap<Validator, String>(); // 利用Map保存與每一個Validator相對應的驗證消息
    childGetters = new ArrayList<Member>();//  保存子類的成員(字段or方法)和構造函數方法的標識信息

    childClassValidators.put( beanClass, this ); //map Map<Class, ClassValidator> childClassValidators;
    Annotation[] classAnnotations = beanClass.getAnnotations();
    for ( int i = 0; i < classAnnotations.length ; i++ ) {
      Annotation classAnnotation = classAnnotations[i];
     Validator beanValidator = createValidator( classAnnotation );//根據Annotation來獲得Validator,參考對該函數的解釋
      if ( beanValidator != null ) beanValidators.add( beanValidator );//保存該Validator
    }
    //build the class hierarchy to look for members in
    Collection<Class> classes = new HashSet<Class>();
    addSuperClassesAndInterfaces( beanClass, classes );//把beanClass的全部超類和實現的接口添加的集合classes中

    //Check on all selected classes
    for ( Class currClass : classes ) {
      Method[] methods = currClass.getDeclaredMethods();// 掃描Method上面的註釋
      for ( int i = 0; i < methods.length ; i++ ) {
        Method method = methods[i];
        createMemberValidator( method ); // 建立方法上的約束實現類(Validator), 參考對該函數的解釋
        Class clazz = method.getReturnType();// 獲得該方法的返回類型
        createChildValidator( resourceBundle, method, clazz );// 建立子類的Validator
      }

      Field[] fields = currClass.getDeclaredFields(); // 掃描Field上面的註釋, 下面和上面Method的實現同樣
      for ( int i = 0; i < fields.length ; i++ ) {
        Field field = fields[i];
        createMemberValidator( field );
        Class clazz = field.getType();
        createChildValidator( resourceBundle, field, clazz );
      }
    }
  }

  private void addSuperClassesAndInterfaces(Class clazz, Collection<Class> classes) {
    for ( Class currClass = clazz; currClass != null ; currClass = currClass.getSuperclass() ) {
      if ( ! classes.add( currClass ) ) return;
      Class[] interfaces = currClass.getInterfaces();
      for (Class interf : interfaces) {
        addSuperClassesAndInterfaces( interf, classes );
      }
    }
  }

  /**
   * 建立內嵌類的Validator. 若是該內嵌類被Valid Annotation 註釋的話則
   * 建立另一個ClassValidator
   * @param resourceBundle
   * @param member
   * @param clazz
   */
  private void createChildValidator(ResourceBundle resourceBundle, Member member, Class clazz) {
    if ( ( (AnnotatedElement) member ).isAnnotationPresent( Valid.class ) ) {
      setAccessible( member );
      childGetters.add( member );
      if ( !childClassValidators.containsKey( clazz ) ) {
        new ClassValidator( clazz, resourceBundle, childClassValidators );
      }
    }
  }

  /**
   * 利用傳入的Method(實現了AnnotatedElement, GenericDeclaration, Member接口)
   * 獲得 方法上的Annotations 而後利用私有方法createValidator(Annotation a)來建立
   * 每個Annotation 的實現類 Validator 並保存Validator和member
   * @param member
   */
  private void createMemberValidator(Member member) {
    Annotation[] memberAnnotations = ( (AnnotatedElement) member ).getAnnotations();
    for ( int j = 0; j < memberAnnotations.length ; j++ ) {
      Annotation methodAnnotation = memberAnnotations[j];
      Validator propertyValidator = createValidator( methodAnnotation );
      if ( propertyValidator != null ) {
        memberValidators.add( propertyValidator );
        setAccessible( member ); // 設置訪問屬性
        memberGetters.add( member );
      }
    }
  }

  private static void setAccessible(Member member) {
    if ( !Modifier.isPublic( member.getModifiers() ) ) {
      ( (AccessibleObject) member ).setAccessible( true );
    }
  }

  /**
   * 該方法產生了該Annotation的約束實現類 並初始化該類對應的消息
   */
  private Validator createValidator(Annotation annotation) {
    try {
      //獲得ValidatorClass Annotation
      ValidatorClass validatorClass = annotation.annotationType().getAnnotation( ValidatorClass.class );
      if ( validatorClass == null ) {
        return null;
      }
      // 而後 利用ValidatorClass Annotation 來獲得裏面的值(即實現該註釋的Class),
      //再利用Class 構造一個instance
      Validator beanValidator = validatorClass.value().newInstance();
      beanValidator.initialize( annotation ); // 初始化Annotation中的參數(注意:在自定義約束中該方法有你來實現)
      String messageTemplate = (String) annotation.getClass()
          .getMethod( "message", (Class[]) null )
          .invoke( annotation );  // 取得 constraint descriptor  中的message 的值
      String message = replace( messageTemplate, annotation ); // 初始化取得的模板消息 請參考 replace函數
      messages.put( beanValidator, message ); // 把message 放在map中,以便使用
      return beanValidator; // 返回 產生的Validator
    }
    catch (Exception e) {
      throw new IllegalArgumentException( "could not instantiate ClassValidator", e );
    }
  }

  public boolean hasValidationRules() {
    return beanValidators.size() != 0 || memberValidators.size() != 0;
  }

  /**
   * apply constraints on a bean instance and return all the failures.
   */
  public InvalidValue[] getInvalidValues(T bean) {
    return this.getInvalidValues( bean, new IdentitySet() );
  }

  /**
   * apply constraints on a bean instance and return all the failures.
   */
  protected InvalidValue[] getInvalidValues(T bean, Set<Object> circularityState) {
    if ( circularityState.contains( bean ) ) {  // 該if else 是和Hibernate Core由關的,
      return EMPTY_INVALID_VALUE_ARRAY; //Avoid circularity
    }
    else {
      circularityState.add( bean );
    }

    if ( !beanClass.isInstance( bean ) ) { // 若是beanClass不是該bean的實例,則拋出異常
      throw new IllegalArgumentException( "not an instance of: " + bean.getClass() );
    }

    List<InvalidValue> results = new ArrayList<InvalidValue>();

    for ( int i = 0; i < beanValidators.size() ; i++ ) { // 驗證類級別的約束
      Validator validator = beanValidators.get( i );
      if ( !validator.isValid( bean ) ) { //調用isValid方法,若是沒有經過則添加到list<InvalidValue>中
                        //若是是自定義約束則isValid方法 由你來實現
        results.add( new InvalidValue( messages.get( validator ), beanClass, null, bean, bean ) );
      }
    }

    for ( int i = 0; i < memberValidators.size() ; i++ ) {//驗證方法級別的約束
      Member getter = memberGetters.get( i );
      if ( Hibernate.isPropertyInitialized(bean, getter.getName() ) ) {// ? 檢查該屬性是否已初始化
        Object value = getMemberValue( bean, getter );//利用反射 取得該屬性的值
        Validator validator = memberValidators.get( i ); //取得該約束的驗證明現類
        if ( !validator.isValid( value ) ) {//調用isValid方法,若是沒有經過則添加到list<InvalidValue>中
          String propertyName = getPropertyName( getter );
          results.add( new InvalidValue( messages.get( validator ), beanClass, propertyName, value, bean ) );
        }
      }
    }

    for ( int i = 0; i < childGetters.size() ; i++ ) {// 處理子類類
      Member getter = childGetters.get( i );
      if ( Hibernate.isPropertyInitialized(bean, getter.getName() ) ) { //檢查該屬性是否已初始化
        Object value = getMemberValue( bean, getter );
        if ( value != null && Hibernate.isInitialized( value ) ) {
          String propertyName = getPropertyName( getter );
          InvalidValue[] invalidValues = getClassValidator( value )
              .getInvalidValues( value, circularityState );// 經過參數value 獲得 Class, 而後由Class做爲key          //在childClassValidators map中獲得其ClassValidator
                                     //若是不存在 則建立新的 ,而後再調用ClassValidator的getInvalidValues方法
          // 注意在調用getInvalidValues方法時 用到了circularityState 參數, 當調用循環一週時 返回(遞歸結束)
          for ( InvalidValue invalidValue : invalidValues ) {
            invalidValue.addParentBean( bean, propertyName );
            results.add( invalidValue ); //添加的結果中
          }
        }
      }
    }

    return results.toArray( new InvalidValue[results.size()] ); //返回InvalidValue數組
  }

  /**
   * 經過參數value 獲得 Class, 而後由Class做爲key 在childClassValidators map中獲得其ClassValidator
   * 若是不存在 則建立新的 而後返回
   * @param value
   * @return
   */
  private ClassValidator getClassValidator(Object value) {
    Class clazz = value.getClass();
    ClassValidator validator = childClassValidators.get( clazz );
    if ( validator == null ) { //handles polymorphism
      validator = new ClassValidator( clazz );
    }
    return validator;
  }

  /**
   * Apply constraints of a particular property on a bean instance and return all the failures.
   * Note this is not recursive.
   * 驗證單個屬性的約束.
   */
  //TODO should it be recursive ?
  public InvalidValue[] getInvalidValues(T bean, String propertyName) {
    List<InvalidValue> results = new ArrayList<InvalidValue>();

    for ( int i = 0; i < memberValidators.size() ; i++ ) {
      Member getter = memberGetters.get( i );
      if ( getPropertyName( getter ).equals( propertyName ) ) {// 驗證該屬性的約束
        Object value = getMemberValue( bean, getter );
        Validator validator = memberValidators.get( i );
        if ( !validator.isValid( value ) ) {
          results.add( new InvalidValue( messages.get( validator ), beanClass, propertyName, value, bean ) );
        }
      }
    }

    return results.toArray( new InvalidValue[results.size()] );
  }

  /**
   * Apply constraints of a particular property value of a bean type and return all the failures.
   * The InvalidValue objects returns return null for InvalidValue#getBean() and InvalidValue#getRootBean()
   * Note this is not recursive.
   * 驗證 value 是否知足當前屬性的約束.
   */
  //TODO should it be recursive?
  public InvalidValue[] getPotentialInvalidValues(String propertyName, Object value) {
    List<InvalidValue> results = new ArrayList<InvalidValue>();

    for ( int i = 0; i < memberValidators.size() ; i++ ) {
      Member getter = memberGetters.get( i );
      if ( getPropertyName( getter ).equals( propertyName ) ) {
        Validator validator = memberValidators.get( i );
        if ( !validator.isValid( value ) ) {
          results.add( new InvalidValue( messages.get( validator ), beanClass, propertyName, value, null ) );
        }
      }
    }

    return results.toArray( new InvalidValue[results.size()] );
  }

  private Object getMemberValue(T bean, Member getter) {
    Object value;
    try {
      value = getValue( getter, bean );
    }
    catch (Exception e) {
      throw new IllegalStateException( "Could not get property value", e );
    }
    return value;
  }

  private Object getValue(Member member, T bean) throws IllegalAccessException, InvocationTargetException {
    if ( member instanceof Field ) {
      return ( (Field) member ).get( bean );
    }
    else if ( member instanceof Method ) {
      return ( (Method) member ).invoke( bean );
    }
    else {
      throw new AssertionFailure( "Unexpected member: " + member.getClass().getName() );
    }
  }

  public String getPropertyName(Member member) {
    //Do no try to cache the result in a map, it's actually much slower (2.x time)
    String propertyName;
    if ( member instanceof Field ) {
      propertyName = member.getName();
    }
    else if ( member instanceof Method ) {
      propertyName = member.getName();
      if ( propertyName.startsWith( "is" ) ) {
        propertyName = Introspector.decapitalize( propertyName.substring( 2 ) );
      }
      else if ( propertyName.startsWith( "get" ) ) {
        propertyName = Introspector.decapitalize( propertyName.substring( 3 ) );
      }
      //do nothing for non getter method, in case someone want to validate a PO Method
    }
    else {
      throw new AssertionFailure( "Unexpected member: " + member.getClass().getName() );
    }
    return propertyName;
  }

  private String replace(String message, Annotation parameters) {
    StringTokenizer tokens = new StringTokenizer( message, "{}", true );
    StringBuilder buf = new StringBuilder( 30 );
    boolean escaped = false;
    while ( tokens.hasMoreTokens() ) {
      String token = tokens.nextToken();
      if ( "{".equals( token ) ) {
        escaped = true;
      }
      else if ( "}".equals( token ) ) {
        escaped = false;
      }
      else if ( !escaped ) {
        buf.append( token );
      }
      else {
        Method member;
        try {
          member = parameters.getClass().getMethod( token, (Class[]) null );
        }
        catch (NoSuchMethodException nsfme) {
          member = null;
        }
        if ( member != null ) {
          try {
            buf.append( member.invoke( parameters ) );
          }
          catch (Exception e) {
            throw new IllegalArgumentException( "could not render message", e );
          }
        }
        else if ( messageBundle != null ) {
          String string = messageBundle.getString( token );
          if ( string != null ) buf.append( replace( string, parameters ) );
        }
      }
    }
    return buf.toString();
  }

  /**
   * apply the registred constraints rules on the hibernate metadata (to be applied on DB schema...)
   *該方法是與實體類綁定的 不推薦使用 有興趣的讀者能夠本身研究一下
   * @param persistentClass hibernate metadata
   */
  public void apply(PersistentClass persistentClass) {  
   //源代碼省略
  }

  /**
   * 斷言該bean 是否符合全部約束. 負責拋出異常
   * @param bean
   */
  public void assertValid(T bean) {
    InvalidValue[] values = getInvalidValues( bean );
    if ( values.length > 0 ) {
      throw new InvalidStateException( values );
    }
  }

  /**
   * 該方法應該是序列化ResourceBundle的 爲private方法 但並無用到, 不知道爲何 可能之後會有用
   * @param oos
   * @throws IOException
   */
  private void writeObject(ObjectOutputStream oos) throws IOException {
    ResourceBundle rb = messageBundle;
    if ( rb != null && ! ( rb instanceof Serializable ) ) {
      messageBundle = null;
      if ( ! defaultResourceBundle )
        log.warn( "Serializing a ClassValidator with a not serializable ResourceBundle: ResourceBundle ignored" );
    }
    oos.defaultWriteObject();
    oos.writeObject( messageBundle );
    messageBundle = rb;
  }

  /**
   * 該方法應該是讀取序列化的ResourceBundle的 爲private方法 但並無用到,不知道爲何 可能之後會有用
   * @param ois
   * @throws IOException
   * @throws ClassNotFoundException
   */
  private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    ResourceBundle rb = (ResourceBundle) ois.readObject();
    if (rb == null) rb = getDefaultResourceBundle();
    initValidator( beanClass, new HashMap<Class, ClassValidator>(), rb );
  }
}



還記得咱們在驗證時候所寫的代碼嗎:

 ClassValidator<Person> classValidator = new ClassValidator<Person> (Person.class);
InvalidValue[] validMessages = classValidator.getInvalidValues(p);

只調用了classValidator的getInvalidValues(p);方法 咱們就獲得了InvalidValue[] validMessages, 該方法作了什麼事情呢? 有上面的註釋看起來就輕鬆多了 ^_^.首先:在你建立ClassValidator時, 會調用ClassValidator的構造方法 她一供有三個構造函數 :  有兩個構造函數(一個傳遞要驗證的類爲參數,一個還要加上你自定義的ResourceBundle)來供咱們使用, 還有一個protected 的構造函數. 在構造函數中都作了寫什麼呢? 第一: 把要驗證的類保存起來,第二:決定使用的消息資源,若是你提供了本身的ResourceBundle 就使用自定義消息,不然使用默認的消息資源.第三: 根據java反射機制,利用Annotation初始化全部的驗證約束類,而後驗證是否知足驗證條件.下面咱們來關注一下initValidator 方法,看看是如何初始化驗證約束類的. 如今請仔細看看 initValidator 裏面的註釋.而後在繼續往下看.^_^通 過上面的分析,能夠看到在 initValidator函數中,初始化了你傳入類的全部的約束Annotations 的相關的東東(如: 其約束驗證明現類, 若是有內嵌的類,若是該類被Valid Annotation註釋的話 也構造一個內嵌類的Validator 並初始化其相關的東東 如此遞歸執行下去).該函數執行完後,能夠說構造了一個以你傳入的類爲跟的 約束註釋樹(自創的名詞,不瞭解也不要緊 ^_^),而後由此樹來逐個驗證沒有個約束.此時已經具有驗證約束的條件了.你只有調用 classValidator.getInvalidValues(p)方法就能夠驗證類p 上的全部約束了.GetInvalidValues()方法有作了什麼呢, 如今你要再回到上面看看她的註釋了  ^_^:怎 麼樣如今知道 GetInvalidValues 作了什麼了吧.她就是取出 約束註釋樹中的每個約束註釋(分爲 類註釋, 方法註釋, 屬性註釋 和內嵌類註釋 (也就是類裏面的類屬性)),並驗證相應的成員是否知足該約束註釋的要求,也就是調用Validator的isValid() 方法.最後用不知足要求的 成員信息構造InvalidValue 數組 並返. ClassValidator 類咱們基本上已經講解完了,剩下的該Validatro裏面的就是一些內建的約束Annotation和約束驗證明現類了,這些看看前一篇文章就明白怎麼 回事了.到此 HibernateValidator 框架基本上分析完了. 經過分析該框架.讓我看到了Annotation的一種高級用法的實現機制,和反射機制的巧妙應用,以及幾個巧妙的設計模式(我就不在舉例了 你們能夠相互探討一下). 你從中學到了什麼呢?對想把Hibernate Validator作成一個獨立框架的幾點說明:1.去掉apply 函數.2. 在getPropertyName 和 getMemberValue 中 若是獲得的值爲null 則拋出org.hibernate.AssertionFailure異常. 能夠重寫該異常,或者從Hibernate源代碼中提取(建議重寫).3. 用到了Hibernate.isPropertyInitialized(Object o,String name)方法 判斷該類(o)的屬性(name)是否以及加載的, 該函數的doc 註釋爲 Check if the property is initialized. If the named property does not exist or is not persistent, this method always returns true.能夠替換爲判斷該屬性(name)是否爲null, null即表明沒有賦初值(可能違反約束);不然驗證該值是否違反約束.4.裏面還用到了org.hibernate.util.IdentitySet 一個set實現,能夠本身實現或者從Hibernate中提取(推薦提取);這樣一個獨立的Validation frameWork 就出來了. 不依賴任何第三方代碼,徹底能夠做爲你本身的驗證框架在項目中使用.

相關文章
相關標籤/搜索