欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

struts2 18拦截器详解(十八)

程序员文章站 2023-01-23 09:10:25
annotationvalidationinterceptor      annotationvalidationinterceptor拦截器处于defaultstac...
annotationvalidationinterceptor

 

   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方法中使用代码进行校验。