struts2 18拦截器详解(十八)
annotationvalidationinterceptor拦截器处于defaultstack第十七的位置,主要是用于数据校验的,该拦截器继承自validationinterceptor拦截器增加了在方法上使用注解取消校验功能。validationinterceptor又继承自methodfilterinterceptor。前面继承自methodfilterinterceptor的拦截器中都没有讲methodfilterinterceptor,在annotationvalidationinterceptor配置中传递了一个名为excludemethods的参数,这个参数就是提交到methodfilterinterceptor中的,用于指定哪些方法是不需要进行校验的。所以这里先讲解methodfilterinterceptor拦截器,下面是methodfilterinterceptor的intercept:
[java]
@override
public string intercept(actioninvocation invocation) throws exception {
if (applyinterceptor(invocation)) {
return dointercept(invocation);
}
return invocation.invoke();
}
这里在执行invocation.invoke();之前调用了applyinterceptor判断是否要应用上该拦截器,下面看一下applyinterceptor方法:
[java]
protected boolean applyinterceptor(actioninvocation invocation) {
string method = invocation.getproxy().getmethod();
//真正判断的方法是methodfilterinterceptorutil.applymethod方法,把排除的方法集合与包含的方法集合与action要执行的方法名传入
//该方法把字符串转成了正则表达式对该方法进行匹配,逻辑不难,但判断代码比较多,所以讲到这吧...
boolean applymethod = methodfilterinterceptorutil.applymethod(excludemethods, includemethods, method);
if (log.isdebugenabled()) {
if (!applymethod) {
log.debug("skipping interceptor... method [" + method + "] found in exclude list.");
}
}
return applymethod;
}
所以所有继承自methodfilterinterceptor的拦截器都可以设置excludemethods与includemethods参数用于指定哪些方法要应用上该拦截器,哪些方法不需要应用上该拦截器,对于annotationvalidationinterceptor就是哪些方法要进行校验与哪方法不需要进行校验。annotationvalidationinterceptor在defaultstack中的配置为:
[html]
<interceptor-ref name="validation">
<param name="excludemethods">input,back,cancel,browse</param>
</interceptor-ref>
即排除input,back,cancel,browse这四个方法外,其它执行的action方法都要进行校验。
现在假设要进行校验,所以会执行annotationvalidationinterceptor的dointercept方法,下面是该方法源码:
[java]
protected string dointercept(actioninvocation invocation) throws exception {
//获取当前执行的action
object action = invocation.getaction();
if (action != null) {//如果action不为null
method method = getactionmethod(action.getclass(), invocation.getproxy().getmethod());//获取action要执行的方法
//获取action中加了skipvalidation注解的方法集合
collection<method> annotatedmethods = annotationutils.getannotatedmethods(action.getclass(), skipvalidation.class);
if (annotatedmethods.contains(method))
return invocation.invoke();//如果当前执行的方法有skipvalidation注解则不进行校验,调用下一个拦截器
//检测是否有覆盖父类标有skipvalidation注解的方法
class clazz = action.getclass().getsuperclass();//获取父类字节码
while (clazz != null) {
annotatedmethods = annotationutils.getannotatedmethods(clazz, skipvalidation.class);//获取父类标有skipvalidation注解的方法集合
if (annotatedmethods != null) {//如果方法不为null
//如果当前要执行的方法是覆盖了父类的方法,而父类方法标有skipvalidation注解,则当前方法也不进行校验
for (method annotatedmethod : annotatedmethods) {
if (annotatedmethod.getname().equals(method.getname())
&& arrays.equals(annotatedmethod.getparametertypes(), method.getparametertypes())
&& arrays.equals(annotatedmethod.getexceptiontypes(), method.getexceptiontypes()))
return invocation.invoke();//调用下一个拦截器
}
}
clazz = clazz.getsuperclass();//获取父类字节码
}
}
//如果要进行校验,继续调用父类的dointercept方法
return super.dointercept(invocation);
}
从上面可以看到如果当前action执行的方法上面如果标注有skipvalidation注解或者其覆盖的方法上标注有skipvalidation注解就不会对该方法进行校验,执行完成后还调用了父类validationinterceptor的dointercept方法,下面该方法源码:
[java]
@override
protected string dointercept(actioninvocation invocation) throws exception {
dobeforeinvocation(invocation);//调用dobeforeinvocation方法
return invocation.invoke();//调用下一个拦截器
}
dobeforeinvocation(invocation)方法源码:
protected void dobeforeinvocation(actioninvocation invocation) throws exception {
object action = invocation.getaction();//获取当前执行的action
actionproxy proxy = invocation.getproxy();//获取actionproxy对象
//the action name has to be from the url, otherwise validators that use aliases, like
//myactio-someaction-validator.xml will not be found, see ww-3194
string context = proxy.getactionname();//获取action名称
string method = proxy.getmethod();//获取执行action的方法名称
//省略...
//declarative默认为true
if (declarative) {
if (validateannotatedmethodonly) {//validateannotatedmethodonly默认为false
actionvalidatormanager.validate(action, context, method);
} else {
actionvalidatormanager.validate(action, context);//所以执行这里
}
}
//如果action实现了validateable接口,actionsupport实现了validateable接口
if (action instanceof validateable && programmatic) {//programmatic默认为true
exception exception = null;
//强转
validateable validateable = (validateable) action;
if (log.isdebugenabled()) {
log.debug("invoking validate() on action "+validateable);
}
try {//调用有validate,validatedo前缀的方法
prefixmethodinvocationutil.invokeprefixmethod(
invocation,
new string[] { validate_prefix, alt_validate_prefix });
}
catch(exception e) {
// if any exception occurred while doing reflection, we want
// validate() to be executed
log.warn("an exception occured while executing the prefix method", e);
exception = e;
}
//alwaysinvokevalidate默认为true,总是调用action的validate方法
if (alwaysinvokevalidate) {
validateable.validate();
}
if (exception != null) {
// rethrow if something is wrong while doing validatexxx / validatedoxxx
throw exception;
}
}
}
因为struts2提供了声明式校验的功能,即使用xml文件对提交过来的数据进行校验,而这种声明式校验就是由actionvalidatormanager.validate(action, context);这句代码实现的调用actionvalidatormanager的validate方法,其方法内部就是去查找相应的xml校验文件,解析xml校验文件生成com.opensymphony.xwork2.validator.validator检验器对象,然后对象提交的数据进行校验,如果校验有些数据不合法则会将相应的错误信息通过addfielderror添加字段错误信息打印到控制台。因为声明式校验功能涉及寻找xml校验文件,解析xml校验文件生成校验器对象,再使用校验器对象进行校验数据,里面还添加了校验器缓存,所以里面的代码量很大,在这只能
讲大概原理,具体细节有兴趣可以自己去研究。
如果action继承自actionsupport类(通常),则实现了validateable接口,接下来就会调用带有validate或validatedo前缀的校验方法,就是通过prefixmethodinvocationutil这个工具类调用的,这个工具类前面我们已经遇到过了,在讲解prepareinterceptor拦截器的时候,会调用带有prepare或preparedo前缀的方法。带有validate或validatedo前缀的校验方法如果同时存在的话只会执行带有validate前缀的方法,这是prefixmethodinvocationutil
这个工具类内部代码决定的。validationinterceptor的alwaysinvokevalidate属性默认为true,所以action的validate方法总是会调用的,即validateable.validate();这句代码会执行。在validate方法中使用代码进行校验。
上一篇: 女人经常喝红糖水,五大好处来找你
下一篇: 桃会上火吗,哪些人不宜吃桃