2008-03-26
3.7.2验证元数据(ValidationMetadata)
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类很简单:
如何处理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中是Validate的copy,也就是把注释中的数据一一对应地放在ValidationMetadata中的字段中。我们可能通过程序来修改某些属性。在@Validate(required=true, on="!delete") 中我们可以看出on的事件 可以采用!表示除了delete 事件的所有其它事件。onIsPositive就是保存这个信息。我们在程序中可能通过字段的ValidationMetadata的set方法来动态修改静态的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.
发表评论
- 浏览: 6324 次
- 性别:

- 来自: 南昌

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
jquery core 源码分析
樓主夠狠,通讀了源代碼。肯定獲益非淺。
-- by zhouyrt -
Ext.template分析
for指令,if指令等 在 Ext.XTemplate 中实现了
-- by xxf_cz -
jquery core 源码分析
打倒标题党.
-- by jonee -
Ext.Event分析(二)
var args = Array.prototype.slice.call(ar ...
-- by jljlpch -
Ext.Event分析(三)
请排一下版,看起来太费劲了
-- by cagway






评论排行榜