Ryan's Java world!

something about Java and opensource!

BlogJava 首页 新随笔 联系 聚合 管理
  51 Posts :: 25 Stories :: 59 Comments :: 0 Trackbacks

任何获得Matrix授权的网站,转载请保留以下作者信息和链接:
作者:icess(作者的blog:http://blog.matrix.org.cn/page/icess)
关键字:Hibernate Validator

在前一篇文章 < Hibernate Validator 简介 > http://www.matrix.org.cn/resource/article/44/44153_Hibernate%20Validator%20.html中,我们看到了Hibernate Validator的使用方法,和自定义验证Annotation的实现以及错误消息的国际化等常见问题.

在使用如此优雅的属性验证框架的同时,你是否想了解她的细节呢?她究竟是怎么实现的呢? 那么现在就跟随我来探探她的内核吧!

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() !=  || 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(  ) );
       }
       else if  ( propertyName.startsWith(  "get"  ) ) {
         propertyName = Introspector.decapitalize( propertyName.substring(  ) );
       }
       //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 >  ) {
       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 就出来了. 不依赖任何第三方代码,完全可以作为你自己的验证框架在项目中使用.

PS: 关于在实体类上(持久化层)使用Validator是否有好处,大家可以到如下连接看看:http://www.hibernate.org.cn/viewtopic.php?t=18131

我也在Matrix Hibernate 论坛开了一讨论贴 请大家走过路过都看看:http://www.matrix.org.cn/thread.shtml?topicId=36657&forumId=23 让我们更高效的使用 Validator.

 

 

posted on 2006-05-18 15:17 冰雨 阅读(2101) 评论(0)  编辑  收藏 所属分类: Opensource

只有注册用户登录后才能发表评论。


网站导航:
 

JSF中文技术文摘