struts2 18拦截器详解(十八) --- AnnotationValidationInterceptor

AnnotationValidationInterceptor

   AnnotationValidationInterceptor拦截器处于defaultStack第十七的位置,主要是用于数据校验的,该拦截器继承自ValidationInterceptor拦截器增加了在方法上使用注解取消校验功能。ValidationInterceptor又继承自MethodFilterInterceptor。前面继承自MethodFilterInterceptor的拦截器中都没有讲MethodFilterInterceptor,在AnnotationValidationInterceptor配置中传递了一个名为excludeMethods的参数,这个参数就是提交到MethodFilterInterceptor中的,用于指定哪些方法是不需要进行校验的。所以这里先讲解MethodFilterInterceptor拦截器,下面是MethodFilterInterceptor的intercept源码:

@Override
public String intercept(ActionInvocation invocation) throws Exception {
    if (applyInterceptor(invocation)) {
        return doIntercept(invocation);
    } 
    return invocation.invoke();
}

   这里在执行invocation.invoke();之前调用了applyInterceptor判断是否要应用上该拦截器,下面看一下applyInterceptor方法:

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中的配置为:


<interceptor-ref name="validation">
    <param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>

   即排除input,back,cancel,browse这四个方法外,其它执行的Action方法都要进行校验。
现在假设要进行校验,所以会执行AnnotationValidationInterceptor的doIntercept方法,下面是该方法源码:

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方法,下面该方法源码:

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

   到这里doBeforeInvocation方法就执行完成了,整个AnnotationValidationInterceptor逻辑也就执行完成,最后调用invocation.invoke();调用下一个拦截器......

你可能感兴趣的:(struts2,Interceptor)