在上一集中我们讲到了属性验证器,相对比较简单.
在这一集中我们继续讲visitor验证器,该验证器的用处是当action 中的属性不是基本类型和字符串时,如,对象,数组,集合等.
我们先新建一个类Person,就两个属性.
private int id;
private String name;
省略了相应的get/set方法
再新建一个Action--PersonAction,继承自ActionSupport.
代码如下:
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
增加一个Person类型的属性person.
在struts.xml文件中配置action,
<action name="person" class="action.main.PersonAction">
<result name="input">/index.jsp</result>
</action>
再在PersonAction同目录下新建一个PersonAction-validation.xml文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="person"> <field-validator type="visitor"> <param name="context">per</param> <message>action</message> </field-validator> </validators>
这里还有一个参数是appendPrefix,这个意思是action中这个验证器的消息将会最终添加到person验证器的消息开头.默认值是true.context这个参数的值可以是任意的,但是要跟下面这个文件的名称一致,如果不指定的话,将会使用action的名称.
然后再在Person同目录下新建一个Person-per-validation.xml文件,代码如下:
<validators> <field name="name"> <field-validator type="fieldexpression"> <param name="expression">equals("qqq")</param> <message>-----${name}person.name${{"aa","aaa","bb","cc"}}</message> </field-validator> </field> </validators>
好了,代码就是这样,下面看一下运行效果.
在浏览器中输入http://localhost:8080/a/person.action?person.name=bbbb,可以看到运行结果如下:
可以看到已经将person属性給拦截了,因为是name是"bbbb",所以不满足表达式,"action" 被添加到了最后的消息开头,然后输出了person验证器中的消息.
使用起来还是很简单的.
下面就我的理解我来解释一下context这个字符串的作用,
官方文档上的说法是,ActionValidatorManager将使用这个参数来查找与action相关的ValidatorConfigs(处理validator 配置文件)类.可以用来根据 ActionClass-context-validation.xml这个模式来查找validation.xml文件,默认的值是action的名字,validator拦截器給我们提供了一个方法,
protected String getValidationContext(ActionProxy proxy) { // This method created for WW-3753 return proxy.getActionName(); }
注意到了没有,这是protected的,亲,你懂的.
意味着我们可以重写这个方法,根据我们自己的规则去返回这个context給struts2使用.
比如,我们的action有不同的验证文件,你可以根据条件来返回不一样的context字符串,比如,如果sex是男,就返回"nan",是女就返回"nv",你懂的.
下面上一段代码,看一下struts2是如何找验证文件的,
private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) { List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>(); if (checked == null) { checked = new TreeSet<String>(); } else if (checked.contains(clazz.getName())) { return validatorConfigs; } if (clazz.isInterface()) { Class[] interfaces = clazz.getInterfaces(); for (Class anInterface : interfaces) { validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked)); } } else { if (!clazz.equals(Object.class)) { validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked)); } } // look for validators for implemented interfaces Class[] interfaces = clazz.getInterfaces(); for (Class anInterface1 : interfaces) { if (checked.contains(anInterface1.getName())) { continue; } validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile)); if (context != null) { validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile)); } checked.add(anInterface1.getName()); } validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile)); if (context != null) { validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile)); } checked.add(clazz.getName()); return validatorConfigs; }
这里用到了递归,首先看咱们的Class是不是接口,是的话就得到它继承的接口,然后再递归查找.
然后再看我们的Class是不是Object.class,如果不是,得到父类,然后再递归,直到Class 是Object.class,
从这里也可以看出来的是先从最顶层的接口开始得到validation配置,接口找完之后,再从顶层的Object 开始查找validation配置,接下来就是具体的开始找配置文件了.
buildClassValidatorConfigs逻辑主要在这个方法里面,我们看这行代码
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
得到文件名,这样就可以得到PersonAction-validation.xml这个文件的路径了,
上面主要是按类名来得到配置文件,下面就是根据别名了,
如果context不是空的话,就调用buildAliasValidatorConfigs方法,里面有行代码,
String fileName = aClass.getName().replace('.', '/') + "-" + context.replace('/', '-') + VALIDATION_CONFIG_SUFFIX;
看到了吧,如果咱们的context是"nan",那么就可以得到PersonAction-nan-validation.xml文件了.
查找配置文件以及执行验证器,都是在com.opensymphony.xwork2.validator.AnnotationActionValidatorManager这个类里面完成的.
再补充一点,就是我们可以在action中定义validationMethodName和validationDoMethodName这样的方法来进行验证,但是只会被调用一个,比如你的action方法名为save,那么可以定义validationSave这个方法来进行验证操作.
这个方法将会在配置文件验证之后再进行验证.
好了,这次就写到这里了.文笔不好,表达也不清楚,还请见谅,希望能帮到大家.