3.7.2验证元数据(ValidationMetadata)
如何处理actionbean中的属性的annotation的验证是一个值得考虑的问题。在stripes1.5以前所有的验证的annotation都在DefaultActionBeanPropertyBinder中实现。这样就会有一些问题,比如stripes框架的annotation验证处理不能满足系统的业务需求,那么就要扩展stripes的annotation验证处理,在1.5以前是处理不了的,还有一个问题,就是我们的annotation的验证是静态,有可能在系统运行中会根据一些特殊的情况进行调整(对于同一个事件,在某些情况有的验证是不要的,或是要增加的),这个在1.5以前也是处理不了的。
为了解决这两个问题,stripes提出ValidationMetadata和ValidationMetadataProvider来分离解耦。ValidationMetadataProvider是同其它是的组件一样,继承于ConfigurableComponent。也就是说我们可以在web.xml中配置我们自己实现的ValidationMetadataProvider类来完成自己独特的业务。
Stripes默认的DefaultValidationMetadataProvider主要完成两个方面的工作,第一解析该actionbean中所有属性的annotation的验证注释。第二就是把在放在缓存中。我们通过getValidationMetadata(Class<?> beanType)就可以得到该actionbean的validationInfos。
在分析解析该actionbean中所有属性的annotation的验证注释功能之前,我们要先看一下:Validate 、ValidateNestedProperties和ValidationMetadata。:Validate 、ValidateNestedProperties是我们在写程序时要自己的调用的annotation.如:@Validate(required = true, minlength = 6, maxlength = 20, expression = "this == user.pswd", on = "register") private String checkpswd;和@ValidateNestedProperties( {@Validate(field = "name", required = true, minlength = 6, maxlength = 20, on = { "register", "checkName" } ),
@Validate(field = "pswd", required = true, minlength = 6, maxlength = 20, on = "register"), }) private User user;。

Validate类很简单:

 

public @interface Validate {
    //要验证的本属性对象中的属性
     String field() default "";
    //本字段是否加密
    boolean encrypted() default false;
    //本字段是否能为空
    boolean required() default false;
    //本字段在那一些事件方法的验证中有效。默认是全部。
    String[] on() default {};
   //本字段是否能为忽视。
    boolean ignore() default false;
    //本字段的最小长度。
      int minlength() default -1;
//本字段的最大长度。
    int maxlength() default -1;
//本字段的最小值。
   double minvalue() default Double.MIN_VALUE;
//本字段的最大值。  
    double maxvalue() default Double.MAX_VALUE;
   //正则表达式验证。
    String mask() default "";
     //JSP EL表达式验证。
    String expression() default "";
//验证时类型转换,在它之前先进行一些格式方面的验证,比如正则表达式验证,不能为空,//还有长度的验证,类型转换之后,我们要进行一些转换后的验证,比如数值的大小,JSP //EL表达式验证。 
    @SuppressWarnings("unchecked")
	Class<? extends TypeConverter> converter() default TypeConverter.class;
   //验证时出错时的在页面显示的标签,前提是在国际化包中没有找到对应的值。
    String label() default "";

}

ValidateNestedProperties只是一个含有Validate的数组。
public @interface ValidateNestedProperties {
    Validate[] value();
}

ValidationMetadata分析:

public class ValidationMetadata {
    private String property;
    private boolean encrypted;
    private boolean required;
    private Set<String> on;
    private boolean onIsPositive;
    private boolean ignore;
    private Integer minlength, maxlength;
    private Double minvalue, maxvalue;
    private Pattern mask;
    private String expression;
    @SuppressWarnings("unchecked")
	private Class<? extends TypeConverter> converter;
private String label;

。。。。。
}
ValidationMetadata中是Validatecopy也就是把注释中的数据一一对应地放在ValidationMetadata中的字段中。我们可能通过程序来修改某些属性。在@Validate(required=true, on="!delete") 中我们可以看出on的事件 可以采用!表示除了delete 事件的所有其它事件。onIsPositive就是保存这个信息。我们在程序中可能通过字段的ValidationMetadataset方法来动态修改静态的annotation的注释验证。 接下来,我们就要分析了Map<String, ValidationMetadata> loadForClass(Class<?> beanType),它负责解析annotation,把相关的信息放在ValidationMetadata中去。
 
protected Map<String, ValidationMetadata> loadForClass(Class<?> beanType) {
      //构建属性名和ValidationMetadata对应的Map.
    Map<String, ValidationMetadata> meta = new HashMap<String, ValidationMetadata>();
       //构建已经处理过的属性名Set,用来判断,使不重复处理.当父类和子类相同的时//候,就只用子类的annotation.而不处理父类
    Set<String> seen = new HashSet<String>();
try {
     //遍历该类及所有父类
        for (Class<?> clazz = beanType; clazz != null;
 clazz = clazz.getSuperclass()) {
               PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
       //遍历当前类的所有属性及其setter,getter方法。
    for (PropertyDescriptor pd : pds) {
                    String propertyName = pd.getName();//属性名
                    Method accessor = pd.getReadMethod();//getter方法
                    Method mutator = pd.getWriteMethod();//setter方法
                  Field field = null;//本属性的字段
                 try {  field = clazz.getDeclaredField(propertyName);
                    } catch (NoSuchFieldException e) {     }
   
//下面三个boolean是判断在那些地方为该属性做了验证的注释。onAccessor是在getter方法前,onMutator是在setter方法前,onField是在字段前。
    boolean onAccessor = accessor != null
        && Modifier.isPublic(accessor.getModifiers())
        && accessor.getDeclaringClass().equals(clazz)
        && (accessor.isAnnotationPresent(Validate.class) || accessor
          .isAnnotationPresent(ValidateNestedProperties.class));

   boolean onMutator = mutator != null
      && Modifier.isPublic(mutator.getModifiers())
      && mutator.getDeclaringClass().equals(clazz)
      && (mutator.isAnnotationPresent(Validate.class) || mutator
        .isAnnotationPresent(ValidateNestedProperties.class));
   
   boolean onField = field != null
      && !Modifier.isStatic(field.getModifiers())
      && field.getDeclaringClass().equals(clazz)
      && (field.isAnnotationPresent(Validate.class) || field
       .isAnnotationPresent(ValidateNestedProperties.class));
 
// 下面这一段是判断如果在多个地方注释了annotation,就会进行出错提示处理。
   int count = 0;
 if (onAccessor) ++count;
   if (onMutator) ++count;
   if (onField) ++count;

                    // must be 0 or 1
   if (count > 1) {
      StringBuilder buf = new StringBuilder(    "There are conflicting @Validate and/or @ValidateNestedProperties annotations in ")   .append(clazz)  .append(". The following elements are improperly annotated for the '")  .append(propertyName).append("' property:\n");
  
      if (onAccessor) {
         boolean hasSimple = accessor.isAnnotationPresent(Validate.class);
           boolean hasNested = accessor
                 .isAnnotationPresent(ValidateNestedProperties.class);
       buf.append("--> Getter method ").append(accessor.getName()).append(  " is annotated with ");
           
          if (hasSimple)  buf.append("@Validate");
          if (hasSimple && hasNested)    buf.append(" and ");
          if (hasNested)  buf.append("@ValidateNestedProperties");
           buf.append('\n');
            }
          
    if (onMutator) {
      boolean hasSimple = mutator.isAnnotationPresent(Validate.class);
      boolean hasNested = mutator.isAnnotationPresent(ValidateNestedProperties.class);
    buf.append("--> Setter method ").append(mutator.getName()).append(
    " is annotated with ");
      if (hasSimple)  buf.append("@Validate");
      if (hasSimple && hasNested)  buf.append(" and ");
      if (hasNested)  buf.append("@ValidateNestedProperties");
      buf.append('\n');
      }
      if (onField) {
         boolean hasSimple = field.isAnnotationPresent(Validate.class);
          boolean hasNested = field.isAnnotationPresent(ValidateNestedProperties.class);
      buf.append("--> Field ").append(field.getName()).append(
         " is annotated with ");
          if (hasSimple) buf.append("@Validate");
          if (hasSimple && hasNested) buf.append(" and ");
          if (hasNested)   buf.append("@ValidateNestedProperties");
         buf.append('\n');
          }
           throw new StripesRuntimeException(buf.toString());
     }

  //如果出现相同的属性名,就不进行重复的处理。这里的处理是指写入meta中
  if (seen.contains(propertyName))        continue;

      // get the @Validate and/or @ValidateNestedProperties
       Validate simple;
      ValidateNestedProperties nested;
       if (onAccessor) {
           simple = accessor.getAnnotation(Validate.class);
          nested = accessor.getAnnotation(ValidateNestedProperties.class);
           seen.add(propertyName);
         }
      else if (onMutator) {
          simple = mutator.getAnnotation(Validate.class);
          nested = mutator.getAnnotation(ValidateNestedProperties.class);
          seen.add(propertyName);
       }
       else if (onField) {
          simple = field.getAnnotation(Validate.class);
            nested = field.getAnnotation(ValidateNestedProperties.class);
         seen.add(propertyName);
         }
       else {
          simple = null;
           nested = null;
             }

       // add to allow list if @Validate present
   if (simple != null) {
     if (simple.field() == null || "".equals(simple.field())) {
            // 把属性名与之对应的ValidationMetadata放在Meta中。 
  meta.put(propertyName, new ValidationMetadata(propertyName, simple));
        }
      else {
 log.warn("Field name present in @Validate but should be omitted: ", clazz, ", property ", propertyName, ", given field name ",  simple.field());
      }
  }
  // 把nested属性与之对应的ValidationMetadata放在Meta中,nested属性名采用一//user.name.first方式
   if (nested != null) {
          Validate[] validates = nested.value();
        if (validates != null) {
             for (Validate validate : validates) {
                   if (validate.field() != null && !"".equals(validate.field())) {
                          String fullName = propertyName + '.' + validate.field();
                       meta.put(fullName, new ValidationMetadata(fullName, validate));
                }
                else {
                  log.warn("Field name missing from nested @Validate: ", clazz, ", property ", propertyName);
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (RuntimeException e) {
            log.error(e, "Failure checking @Validate annotations ", getClass().getName());
            throw e;
        }
        catch (Exception e) {
            log.error(e, "Failure checking @Validate annotations ", getClass().getName());
            StripesRuntimeException sre = new StripesRuntimeException(e.getMessage(), e);
            sre.setStackTrace(e.getStackTrace());
            throw sre;
        }

// Print out a pretty debug message showing what validations got configured
  StringBuilder builder = new StringBuilder(128);
  for (Map.Entry<String, ValidationMetadata> entry : meta.entrySet()) {
            if (builder.length() > 0) {
                builder.append(", ");
            }
            builder.append(entry.getKey());
            builder.append("->");
            builder.append(entry.getValue());
        }
        log.debug("Loaded validations for ActionBean ", beanType.getSimpleName(), ": ", builder
                .length() > 0 ? builder : "<none>");

        return Collections.unmodifiableMap(meta);
    }
 
上面的函数把 验证的注释存入了Map<String, ValidationMetadata>中,接下来stripes就可以调用该Map. 

 

评论
发表评论

您还没有登录,请登录后发表评论

jljlpch
搜索本博客
最近加入圈子
存档
最新评论