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方法中使用代碼進行校驗。