5. 内部剖析5.1. 类之间的联系ValidatorResults 对象有个 map,以 field 的 getKey() 为键,这个 field 的验证结果 ValidatorResult 对象为值。
ValidatorResult 对象也有个 map, 以 field 的各个 validator 元素的名字为键(在 field 元素的 depends 中定一个 field 的 validator 元素列表),以一个表示验证成功与否的对象为值。
ValidatorResources 对象包含一个 map, 以 Locale 的某种字符串表示为键,FormSet 为值(所以 formset 有多种版本),还包含一个 map,保存了全局常量,以常量名为键,常量值为值;还包含一个 map,以 validator 元素的 name 属性为键 , validatorAction 对象为值。
Formset 对象包含一个 map, 以 form 的 name 属性为键,Form 对象为值;还包含一个 map,以 formset 元素的子元素 Constant 的 name 为键,子元素 Constant 的值为值。
Form 对象包含一个 map, 以 Field 元素对应的 Field 对象的 getKey() 为键,Field 对象为值;另外还拥有一个保存顺序的 field 对象数组。
field 对象拥有一个 map,以 var 的名字为键,var 对象为值。
Validator 对象包含一个 map,以各个 validator 元素的 methodParams 参数列表中的名字为键,相应的对象为值,这个 map 的键和值将会用作调用相应 validator 元素中的 methods 属性指定方法的参数。
通过这些 map,commons-validator 在验证系统各个类间铺了一张类关系表,见下图:
5.2. 如何调用 validatorAction验证规则的 validator 元素定义了 validatorAction,而 field 元素则通过 depends 属性引用了这些 validatorAction。从上面代码样例中的验证主程序可以知道 validator.validate()方法是针对某个 form 元素的,它将对这个 form 元素的各个 field 进行验证,对 field 进行验证也就是调用 field 元素的 depends 属性引用的各个 validator 元素定义的验证方法。
validator 元素使用 classname、method 和 methodParams 三个属性定义了一个验证方法,比如下面的 xml 片断就定义了一个验证整数的验证方法 validateInt,这个方法带有两个参数,类型依次是 java.lang.Object,org.apache.commons.validator.Field。验证方法 validateInt 将在 org.i505.validator.MyTypeValidator 代码中实现。
1
2
3
4
5
| <validator name="int"
classname="org.i505.validator.MyTypeValidator"
method="validateInt"
methodParams="java.lang.Object,org.apache.commons.validator.Field"
msg="errors.int"/>
|
讲了这么多,现在的问题是 validator.validate()方法是如何调用各个验证方法(比如 validateInt)的?
我们用一个顺序图和一段代码剖析这个问题。
顺序图上图是个简要的顺序图,这个顺序图的解释图下:
1. 向 validator 对象增加资源(向资源 map 增加项)
2. 实际验证
对 form 定义的每个 field,调用如下步骤:
#begin
3. 验证一个 field
对 field 的每个 validatoraction,执行如下步骤:
#begin
4. 验证一个 validatoraction
5. 合并验证结果
#end
#end
下面代码详细解释了上面的第四步:验证一个 validatoraction。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| // Add these two Objects to the resources since they reference
// the current validator action and field
hResources.put(VALIDATOR_ACTION_KEY, va);
hResources.put(FIELD_KEY, field);
Class c = getClassLoader().loadClass(va.getClassname());
List lParams = va.getMethodParamsList();
int size = lParams.size();
int beanIndexPos = -1;
int fieldIndexPos = -1;
Class[] paramClass = new Class[size];
Object[] paramValue = new Object[size];
for (int x = 0; x < size; x++) {
String paramKey = (String) lParams.get(x);
if (BEAN_KEY.equals(paramKey)) {
beanIndexPos = x;
}
if (FIELD_KEY.equals(paramKey)) {
fieldIndexPos = x;
}
// There were problems calling getClass on paramValue[]
paramClass[x] = getClassLoader().loadClass(paramKey);
paramValue[x] = hResources.get(paramKey);
}
Method m = c.getMethod(va.getMethod(), paramClass);
// If the method is static we don't need an instance of the class
// to call the method. If it isn't, we do.
if (!Modifier.isStatic(m.getModifiers())) {
try {
if (va.getClassnameInstance() == null) {
va.setClassnameInstance(c.newInstance());
}
}
catch (Exception ex) {
log.error(
"Couldn't load instance "
+ "of class "
+ va.getClassname()
+ ". "
+ ex.getMessage());
}
}
Object result = null;
if (field.isIndexed()) {
Object oIndexed =
PropertyUtils.getProperty(
hResources.get(BEAN_KEY),
field.getIndexedListProperty());
Object indexedList[] = new Object[0];
if (oIndexed instanceof Collection) {
indexedList = ((Collection) oIndexed).toArray();
}
else if (oIndexed.getClass().isArray()) {
indexedList = (Object[]) oIndexed;
}
// Set current iteration object to the parameter array
paramValue[beanIndexPos] = indexedList[pos];
// Set field clone with the key modified to represent
// the current field
Field indexedField = (Field) field.clone();
indexedField.setKey(
ValidatorUtil.replace(
indexedField.getKey(),
Field.TOKEN_INDEXED,
"[" + pos + "]"));
paramValue[fieldIndexPos] = indexedField;
result = m.invoke(va.getClassnameInstance(), paramValue);
results.add(field, va.getName(), isValid(result), result);
if (!isValid(result)) {
return false;
}
}
else {
result = m.invoke(va.getClassnameInstance(), paramValue);
results.add(field, va.getName(), isValid(result), result);
if (!isValid(result)) {
return false;
}
}
|
这段代码首先增加了两个资源:目前正在验证的 field 和 validatoraction,接着实例化验证方法所在类的一个对象,接着按照资源 map 的键 / 值和验证方法的参数类列表构造验证方法的参数列表,最后调用验证方法所在类的一个对象的验证方法。 |