zoukankan      html  css  js  c++  java
  • Struts(二十三):使用声名式验证

    • Struts2工程中的验证分为两种:

    1、基于XWork Validation Framework的声明式验证:Struts2提供了一些基于XWork Validation Framework的内建验证程序.使用这些验证不需要编程,只需要在一个xml文件里对验证程序应该如何工作作出声明就可以了,需要声明的内容包括:

      针对哪个Action或者Model的某个或某些字段验证;

      使用什么验证规则;

      如果验证失败,转向哪个页面,显示什么错误信息。

    2、编程式验证:通过编写代码实现验证用户输入信息。

    • 声明式验证示例:

    1、需要先明确对那个Action或者Model的哪个字段进行验证;

    2、编写配置文件:

      把struts-2.3.31-allstruts-2.3.31appsstruts2-blankWEB-INFsrcjavaexampleLogin-validation.xml

    文件拷贝到对应的包下,并重命名该配置文件为ActionClassName-validation.xml或者ModelClassName-validation.xml

      编写验证规则,参考validation 官网文档:struts-2.3.31-all/struts-2.3.31/docs/docs/validation.html

      在编写文件中可以定义错误消息:

    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.2//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    
    <validators>
        <!-- 基于字段的验证 -->
        <field name="age">
            <field-validator type="int">
                <param name="max">180</param>
                <param name="min">1</param>
                <message>Age must to be between ${min} and ${max}.</message>
            </field-validator>
        </field>
    </validators>

      是否可以把错误消息进行国际化?可以。

      --第一步:在src目录下创建一个i18n.properties文件,编写文件内容:

    ageErrorMsg="Ageu9A8Cu8BC1u5931u8D25,u53D6u503Cu8303u56F4u5FC5u987Bu4F4Du4E8E ${min} u4E0E ${max} u4E4Bu95F4."

      --第二步:修改struts.xml添加配置:

    <constant name="struts.custom.i18n.resources" value="i18n"></constant>

      --第三步:修改com.dx.struts2.myvalidations包下的MyValidationAction-validation.xml:

    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.2//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    
    <validators>
        <!-- 基于字段的验证 -->
        <field name="age">
            <field-validator type="int">
                <param name="max">180</param>
                <param name="min">1</param>
                <message key="ageErrorMsg"></message>
            </field-validator>
        </field>
    </validators>

      -- 访问index.jsp,并在age框中填写1005,提交:

    3、如果验证失败,则转向input的那个result,所以需要配置name=input的result

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
    <struts>
        <constant name="struts.custom.i18n.resources" value="i18n"></constant>
        <package name="default" namespace="/" extends="struts-default">
            <action name="myValidation" class="com.dx.struts2.myvalidations.MyValidationAction">
                <result>/success.jsp</result>
                <result name="input">/index.jsp</result>
            </action>
        </package>
    </struts>

    4、如何显示错误消息?

      如果使用的是非“simple”主题的form标签,则自动显示错误消息;

      如果使用的是“simple”主题的form标签,则可以使用s:fielderror标签或者EL(OGNL表达式)

        <s:form action="myValidation" method="post" theme="simple">
            <s:textfield name="age" label="Age"></s:textfield>
            <s:fielderror fieldName="age"></s:fielderror>
            ${fieldErrors.age[0] }
            <s:submit label="Submit"></s:submit>
        </s:form>
    • 同一个Action类可以应答多个action请求时,多个action请求使用不同的验证规则,怎么办?

    1、为每个不同的action请求定义其对应的验证文件,文件命名规则:ActionClassName-AliasName-validation.xml;

    2、不带别名的的配置文件(ActionClassName-validation.xml)中的验证规则依然会起作用,可以把多个action请求公有的验证规则写到该配置文件中,但如果某个验证规则适用某一个action请求,就不要配置到这里。

      示例:

    struts.xml

        <package name="default" namespace="/" extends="struts-default">
            <action name="myValidation" class="com.dx.struts2.myvalidations.MyValidationAction">
                <result>/success.jsp</result>
                <result name="input">/index.jsp</result>
            </action>
            <action name="myValidation2" class="com.dx.struts2.myvalidations.MyValidationAction" method="execute2">
                <result>/success.jsp</result>
                <result name="input">/index.jsp</result>
            </action>
        </package>

    index.jsp

        <s:form action="myValidation" method="post">
            <s:textfield name="age" label="Age"></s:textfield>
            <s:submit label="Submit"></s:submit>
        </s:form>
        <s:form action="myValidation2" method="post">
            <s:textfield name="age2" label="Age2"></s:textfield>
            <s:submit label="Submit"></s:submit>
        </s:form>

    MyValidationAction.java

    package com.dx.struts2.myvalidations;
    import com.opensymphony.xwork2.ActionSupport;
    
    public class MyValidationAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
    
        private Integer age;
        private Integer age2;
    
        public Integer getAge2() {
            return age2;
        }
        public void setAge2(Integer age2) {
            this.age2 = age2;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
        public Integer getAge() {
            return age;
        }
    
        @Override
        public String execute() throws Exception {
            System.out.println("execute...");
            return SUCCESS;
        }
        
        public String execute2() {
            System.out.println("execute2...");
            return SUCCESS;
        }
    }

    MyValidationAction-myValidation-validation.xml

    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.2//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    
    <validators>
        <!-- 基于字段的验证 -->
        <field name="age">
            <field-validator type="int">
                <param name="max">180</param>
                <param name="min">1</param>
                <message key="ageErrorMsg"></message>
            </field-validator>
        </field>
    </validators>
    View Code

    MyValidationAction-myValidation2-validation.xml

    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.2//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    
    <validators>
        <!-- 基于字段的验证 -->
        <field name="age2">
            <field-validator type="int">
                <param name="max">180</param>
                <param name="min">1</param>
                <message key="ageErrorMsg2"></message>
            </field-validator>
        </field>
    </validators>
    View Code
    • 运行原理分析:

    1、调用struts2的拦截器栈中的validation拦截器(org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor)

    /*
     * $Id$
     *
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *  http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */
    
    package org.apache.struts2.interceptor.validation;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.Collection;
    
    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.inject.Inject;
    import com.opensymphony.xwork2.util.AnnotationUtils;
    import com.opensymphony.xwork2.validator.ValidationInterceptor;
    
    import org.apache.struts2.StrutsConstants;
    
    /**
     * Extends the xwork validation interceptor to also check for a @SkipValidation
     * annotation, and if found, don't validate this action method
     */
    public class AnnotationValidationInterceptor extends ValidationInterceptor {
    
        /** Auto-generated serialization id */
        private static final long serialVersionUID = 1813272797367431184L;
    
        private boolean devMode;
    
        @Inject(StrutsConstants.STRUTS_DEVMODE)
        public void setDevMode(String devMode) {
            this.devMode = "true".equalsIgnoreCase(devMode);
        }
    
        protected String doIntercept(ActionInvocation invocation) throws Exception {
    
            Object action = invocation.getAction();
            if (action != null) {
                Method method = getActionMethod(action.getClass(), invocation.getProxy().getMethod());
                Collection<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethods(action.getClass(), SkipValidation.class);
                if (annotatedMethods.contains(method))
                    return invocation.invoke();
    
                //check if method overwites an annotated method
                Class clazz = action.getClass().getSuperclass();
                while (clazz != null) {
                    annotatedMethods = AnnotationUtils.getAnnotatedMethods(clazz, SkipValidation.class);
                    if (annotatedMethods != null) {
                        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();
                }
            }
    
            return super.doIntercept(invocation);
        }
    
        // FIXME: This is copied from DefaultActionInvocation but should be exposed through the interface
        protected Method getActionMethod(Class actionClass, String methodName) throws NoSuchMethodException {
            Method method = null;
            try {
                method = actionClass.getMethod(methodName, new Class[0]);
            } catch (NoSuchMethodException e) {
                // hmm -- OK, try doXxx instead
                try {
                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                    method = actionClass.getMethod(altMethodName, new Class[0]);
                } catch (NoSuchMethodException e1) {
                    // throw the original one
                    if (devMode) {
                        throw e;
                    }
                }
            }
            return method;
        }
    
    }
    View Code

    2、validation拦截器调用父类拦截器com.opensymphony.xwork2.validator.ValidationInterceptor

    /*
     * Copyright 2002-2007,2009 The Apache Software Foundation.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     *      http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.opensymphony.xwork2.validator;
    
    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.ActionProxy;
    import com.opensymphony.xwork2.Validateable;
    import com.opensymphony.xwork2.inject.Inject;
    import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
    import com.opensymphony.xwork2.interceptor.PrefixMethodInvocationUtil;
    import com.opensymphony.xwork2.util.logging.Logger;
    import com.opensymphony.xwork2.util.logging.LoggerFactory;
    
    /**
     * <!-- START SNIPPET: description -->
     *
     * This interceptor runs the action through the standard validation framework, which in turn checks the action against
     * any validation rules (found in files such as <i>ActionClass-validation.xml</i>) and adds field-level and action-level
     * error messages (provided that the action implements {@link com.opensymphony.xwork2.ValidationAware}). This interceptor
     * is often one of the last (or second to last) interceptors applied in a stack, as it assumes that all values have
     * already been set on the action.
     *
     * <p/>This interceptor does nothing if the name of the method being invoked is specified in the <b>excludeMethods</b>
     * parameter. <b>excludeMethods</b> accepts a comma-delimited list of method names. For example, requests to
     * <b>foo!input.action</b> and <b>foo!back.action</b> will be skipped by this interceptor if you set the
     * <b>excludeMethods</b> parameter to "input, back".
     * 
     * </ol>
     * 
     * <p/> The workflow of the action request does not change due to this interceptor. Rather,
     * this interceptor is often used in conjuction with the <b>workflow</b> interceptor.
     *
     * <p/>
     * 
     * <b>NOTE:</b> As this method extends off MethodFilterInterceptor, it is capable of
     * deciding if it is applicable only to selective methods in the action class. See
     * <code>MethodFilterInterceptor</code> for more info.
     *
     * <!-- END SNIPPET: description -->
     *
     * <p/> <u>Interceptor parameters:</u>
     *
     * <!-- START SNIPPET: parameters -->
     *
     * <ul>
     *
     * <li>alwaysInvokeValidate - Defaults to true. If true validate() method will always
     * be invoked, otherwise it will not.</li>
     *
     * <li>programmatic - Defaults to true. If true and the action is Validateable call validate(),
     * and any method that starts with "validate".
     * </li>
     * 
     * <li>declarative - Defaults to true. Perform validation based on xml or annotations.</li>
     * 
     * </ul>
     *
     * <!-- END SNIPPET: parameters -->
     *
     * <p/> <u>Extending the interceptor:</u>
     *
     * <p/>
     *
     * <!-- START SNIPPET: extending -->
     *
     * There are no known extension points for this interceptor.
     *
     * <!-- END SNIPPET: extending -->
     *
     * <p/> <u>Example code:</u>
     *
     * <pre>
     * <!-- START SNIPPET: example -->
     * 
     * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
     *     &lt;interceptor-ref name="params"/&gt;
     *     &lt;interceptor-ref name="validation"/&gt;
     *     &lt;interceptor-ref name="workflow"/&gt;
     *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
     * &lt;/action&gt;
     * 
     * &lt;-- in the following case myMethod of the action class will not
     *        get validated --&gt;
     * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
     *     &lt;interceptor-ref name="params"/&gt;
     *     &lt;interceptor-ref name="validation"&gt;
     *         &lt;param name="excludeMethods"&gt;myMethod&lt;/param&gt;
     *     &lt;/interceptor-ref&gt;
     *     &lt;interceptor-ref name="workflow"/&gt;
     *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
     * &lt;/action&gt;
     * 
     * &lt;-- in the following case only annotated methods of the action class will
     *        be validated --&gt;
     * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
     *     &lt;interceptor-ref name="params"/&gt;
     *     &lt;interceptor-ref name="validation"&gt;
     *         &lt;param name="validateAnnotatedMethodOnly"&gt;true&lt;/param&gt;
     *     &lt;/interceptor-ref&gt;
     *     &lt;interceptor-ref name="workflow"/&gt;
     *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
     * &lt;/action&gt;
     *
     *
     * <!-- END SNIPPET: example -->
     * </pre>
     *
     * @author Jason Carreira
     * @author Rainer Hermanns
     * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
     * @see ActionValidatorManager
     * @see com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor
     */
    public class ValidationInterceptor extends MethodFilterInterceptor {
    
        private boolean validateAnnotatedMethodOnly;
        
        private ActionValidatorManager actionValidatorManager;
        
        private static final Logger LOG = LoggerFactory.getLogger(ValidationInterceptor.class);
        
        private final static String VALIDATE_PREFIX = "validate";
        private final static String ALT_VALIDATE_PREFIX = "validateDo";
        
        private boolean alwaysInvokeValidate = true;
        private boolean programmatic = true;
        private boolean declarative = true;
    
        @Inject
        public void setActionValidatorManager(ActionValidatorManager mgr) {
            this.actionValidatorManager = mgr;
        }
        
        /**
         * Determines if {@link Validateable}'s <code>validate()</code> should be called,
         * as well as methods whose name that start with "validate". Defaults to "true".
         * 
         * @param programmatic <tt>true</tt> then <code>validate()</code> is invoked.
         */
        public void setProgrammatic(boolean programmatic) {
            this.programmatic = programmatic;
        }
    
        /**
         * Determines if validation based on annotations or xml should be performed. Defaults 
         * to "true".
         * 
         * @param declarative <tt>true</tt> then perform validation based on annotations or xml.
         */
        public void setDeclarative(boolean declarative) {
            this.declarative = declarative;
        }
    
        /**
         * Determines if {@link Validateable}'s <code>validate()</code> should always 
         * be invoked. Default to "true".
         * 
         * @param alwaysInvokeValidate <tt>true</tt> then <code>validate()</code> is always invoked.
         */
        public void setAlwaysInvokeValidate(String alwaysInvokeValidate) {
                this.alwaysInvokeValidate = Boolean.parseBoolean(alwaysInvokeValidate);
        }
    
        /**
         * Gets if <code>validate()</code> should always be called or only per annotated method.
         *
         * @return <tt>true</tt> to only validate per annotated method, otherwise <tt>false</tt> to always validate.
         */
        public boolean isValidateAnnotatedMethodOnly() {
            return validateAnnotatedMethodOnly;
        }
    
        /**
         * Determine if <code>validate()</code> should always be called or only per annotated method.
         * Default to <tt>false</tt>.
         *
         * @param validateAnnotatedMethodOnly  <tt>true</tt> to only validate per annotated method, otherwise <tt>false</tt> to always validate.
         */
        public void setValidateAnnotatedMethodOnly(boolean validateAnnotatedMethodOnly) {
            this.validateAnnotatedMethodOnly = validateAnnotatedMethodOnly;
        }
    
        /**
         * Gets the current action and its context and delegates to {@link ActionValidatorManager} proper validate method.
         *
         * @param invocation  the execution state of the Action.
         * @throws Exception if an error occurs validating the action.
         */
        protected void doBeforeInvocation(ActionInvocation invocation) throws Exception {
            Object action = invocation.getAction();
            ActionProxy proxy = invocation.getProxy();
    
            //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
            //UPDATE:  see WW-3753
            String context = this.getValidationContext(proxy);
            String method = proxy.getMethod();
    
            if (log.isDebugEnabled()) {
                log.debug("Validating "
                        + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName() + " with method "+ method +".");
            }
            
    
            if (declarative) {
               if (validateAnnotatedMethodOnly) {
                   actionValidatorManager.validate(action, context, method);
               } else {
                   actionValidatorManager.validate(action, context);
               }
           }    
            
            if (action instanceof Validateable && programmatic) {
                // keep exception that might occured in validateXXX or validateDoXXX
                Exception exception = null; 
                
                Validateable validateable = (Validateable) action;
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Invoking validate() on action "+validateable);
                }
                
                try {
                    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
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("an exception occured while executing the prefix method", e);
                    }
                    exception = e;
                }
                
                
                if (alwaysInvokeValidate) {
                    validateable.validate();
                }
                
                if (exception != null) { 
                    // rethrow if something is wrong while doing validateXXX / validateDoXXX 
                    throw exception;
                }
            }
        }
    
        @Override
        protected String doIntercept(ActionInvocation invocation) throws Exception {
            doBeforeInvocation(invocation);
            
            return invocation.invoke();
        }
        
        /**
         * Returns the context that will be used by the
         * {@link ActionValidatorManager} to associate the action invocation with
         * the appropriate {@link ValidatorConfig ValidatorConfigs}.
         * <p>
         * The context returned is used in the pattern
         * <i>ActionClass-context-validation.xml</i>
         * <p>
         * The default context is the action name from the URL, but the method can
         * be overridden to implement custom contexts.
         * <p>
         * This can be useful in cases in which a single action and a single model
         * require vastly different validation based on some condition.
         * 
         * @return the Context
         */
        protected String getValidationContext(ActionProxy proxy) {
            // This method created for WW-3753
            return proxy.getActionName();
        }
    
    }
    View Code

    3、在validation拦截器父类拦截器中的doIntercept()方法中,调用了doBeforeInvocation()方法,在该方法中:

           if (declarative) {
               if (validateAnnotatedMethodOnly) {
                   actionValidatorManager.validate(action, context, method);
               } else {
                   actionValidatorManager.validate(action, context);
               }
           }    

    的actionValidatorManager对象就是com.opensymphony.xwork2.validator.AnnotationActionValidatorManager

    /*
     * Copyright 2002-2006,2009 The Apache Software Foundation.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     *      http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.opensymphony.xwork2.validator;
    
    
    import com.opensymphony.xwork2.ActionContext;
    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.ActionProxy;
    import com.opensymphony.xwork2.FileManager;
    import com.opensymphony.xwork2.FileManagerFactory;
    import com.opensymphony.xwork2.XWorkConstants;
    import com.opensymphony.xwork2.config.entities.ActionConfig;
    import com.opensymphony.xwork2.inject.Inject;
    import com.opensymphony.xwork2.util.ClassLoaderUtil;
    import com.opensymphony.xwork2.util.ValueStack;
    import com.opensymphony.xwork2.util.logging.Logger;
    import com.opensymphony.xwork2.util.logging.LoggerFactory;
    import org.apache.commons.lang3.StringUtils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeSet;
    
    /**
     * AnnotationActionValidatorManager is the entry point into XWork's annotations-based validator framework.
     * Validation rules are specified as annotations within the source files.
     *
     * @author Rainer Hermanns
     * @author jepjep
     */
    public class AnnotationActionValidatorManager implements ActionValidatorManager {
    
        /**
         * The file suffix for any validation file.
         */
        protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
    
        private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
        private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
        private static final Logger LOG = LoggerFactory.getLogger(AnnotationActionValidatorManager.class);
    
        private ValidatorFactory validatorFactory;
        private ValidatorFileParser validatorFileParser;
        private FileManager fileManager;
        private boolean reloadingConfigs;
    
        @Inject
        public void setValidatorFactory(ValidatorFactory fac) {
            this.validatorFactory = fac;
        }
    
        @Inject
        public void setValidatorFileParser(ValidatorFileParser parser) {
            this.validatorFileParser = parser;
        }
    
        @Inject
        public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
            this.fileManager = fileManagerFactory.getFileManager();
        }
    
        @Inject(value = XWorkConstants.RELOAD_XML_CONFIGURATION, required = false)
        public void setReloadingConfigs(String reloadingConfigs) {
            this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
        }
    
        public List<Validator> getValidators(Class clazz, String context) {
            return getValidators(clazz, context, null);
        }
    
        public List<Validator> getValidators(Class clazz, String context, String method) {
            final String validatorKey = buildValidatorKey(clazz, context);
            final List<ValidatorConfig> cfgs;
    
            if (validatorCache.containsKey(validatorKey)) {
                if (reloadingConfigs) {
                    validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
                }
            } else {
                validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
            }
    
            // get the set of validator configs
            cfgs = new ArrayList<ValidatorConfig>(validatorCache.get(validatorKey));
    
            ValueStack stack = ActionContext.getContext().getValueStack();
    
            // create clean instances of the validators for the caller's use
            ArrayList<Validator> validators = new ArrayList<Validator>(cfgs.size());
            for (ValidatorConfig cfg : cfgs) {
                if (method == null || method.equals(cfg.getParams().get("methodName"))) {
                    Validator validator = validatorFactory.getValidator(
                            new ValidatorConfig.Builder(cfg)
                                    .removeParam("methodName")
                                    .build());
                    validator.setValidatorType(cfg.getType());
                    validator.setValueStack(stack);
                    validators.add(validator);
                }
            }
    
            return validators;
        }
    
        public void validate(Object object, String context) throws ValidationException {
            validate(object, context, (String) null);
        }
    
        public void validate(Object object, String context, String method) throws ValidationException {
            ValidatorContext validatorContext = new DelegatingValidatorContext(object);
            validate(object, context, validatorContext, method);
        }
    
        public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
            validate(object, context, validatorContext, null);
        }
    
        public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
            List<Validator> validators = getValidators(object.getClass(), context, method);
            Set<String> shortcircuitedFields = null;
    
            for (final Validator validator : validators) {
                try {
                    validator.setValidatorContext(validatorContext);
    
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
                    }
    
                    FieldValidator fValidator = null;
                    String fullFieldName = null;
    
                    if (validator instanceof FieldValidator) {
                        fValidator = (FieldValidator) validator;
                        fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
    
                        if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Short-circuited, skipping");
                            }
    
                            continue;
                        }
                    }
    
                    if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
                        // get number of existing errors
                        List<String> errs = null;
    
                        if (fValidator != null) {
                            if (validatorContext.hasFieldErrors()) {
                                Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
    
                                if (fieldErrors != null) {
                                    errs = new ArrayList<String>(fieldErrors);
                                }
                            }
                        } else if (validatorContext.hasActionErrors()) {
                            Collection<String> actionErrors = validatorContext.getActionErrors();
    
                            if (actionErrors != null) {
                                errs = new ArrayList<String>(actionErrors);
                            }
                        }
    
                        validator.validate(object);
    
                        if (fValidator != null) {
                            if (validatorContext.hasFieldErrors()) {
                                Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
    
                                if ((errCol != null) && !errCol.equals(errs)) {
                                    if (LOG.isDebugEnabled()) {
                                        LOG.debug("Short-circuiting on field validation");
                                    }
    
                                    if (shortcircuitedFields == null) {
                                        shortcircuitedFields = new TreeSet<String>();
                                    }
    
                                    shortcircuitedFields.add(fullFieldName);
                                }
                            }
                        } else if (validatorContext.hasActionErrors()) {
                            Collection<String> errCol = validatorContext.getActionErrors();
    
                            if ((errCol != null) && !errCol.equals(errs)) {
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("Short-circuiting");
                                }
    
                                break;
                            }
                        }
    
                        continue;
                    }
    
                    validator.validate(object);
                } finally {
                    validator.setValidatorContext(null);
                }
    
            }
        }
    
        /**
         * Builds a key for validators - used when caching validators.
         *
         * @param clazz the action.
         * @return a validator key which is the class name plus context.
         */
        protected String buildValidatorKey(Class clazz, String context) {
            ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
            ActionProxy proxy = invocation.getProxy();
            ActionConfig config = proxy.getConfig();
    
            StringBuilder sb = new StringBuilder(clazz.getName());
            sb.append("/");
            if (StringUtils.isNotBlank(config.getPackageName())) {
                sb.append(config.getPackageName());
                sb.append("/");
            }
    
            // the key needs to use the name of the action from the config file,
            // instead of the url, so wild card actions will have the same validator
            // see WW-2996
    
            // UPDATE:
            // WW-3753 Using the config name instead of the context only for
            // wild card actions to keep the flexibility provided
            // by the original design (such as mapping different contexts
            // to the same action and method if desired)
    
            // UPDATE:
            // WW-4536 Using NameVariablePatternMatcher allows defines actions
            // with patterns enclosed with '{}', it's similar case to WW-3753
            String configName = config.getName();
            if (configName.contains(ActionConfig.WILDCARD) || (configName.contains("{") && configName.contains("}"))) {
                sb.append(configName);
                sb.append("|");
                sb.append(proxy.getMethod());
            } else {
                sb.append(context);
            }
            
            return sb.toString();
        }
    
        private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
            String fileName = aClass.getName().replace('.', '/') + "-" + context.replace('/', '-') + VALIDATION_CONFIG_SUFFIX;
    
            return loadFile(fileName, aClass, checkFile);
        }
    
    
        protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
    
            String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
    
            List<ValidatorConfig> result = new ArrayList<ValidatorConfig>(loadFile(fileName, aClass, checkFile));
    
            AnnotationValidationConfigurationBuilder builder = new AnnotationValidationConfigurationBuilder(validatorFactory);
    
            List<ValidatorConfig> annotationResult = new ArrayList<ValidatorConfig>(builder.buildAnnotationClassValidatorConfigs(aClass));
    
            result.addAll(annotationResult);
    
            return result;
    
        }
    
        /**
         * <p>This method 'collects' all the validator configurations for a given
         * action invocation.</p>
         * <p/>
         * <p>It will traverse up the class hierarchy looking for validators for every super class
         * and directly implemented interface of the current action, as well as adding validators for
         * any alias of this invocation. Nifty!</p>
         * <p/>
         * <p>Given the following class structure:
         * <pre>
         *   interface Thing;
         *   interface Animal extends Thing;
         *   interface Quadraped extends Animal;
         *   class AnimalImpl implements Animal;
         *   class QuadrapedImpl extends AnimalImpl implements Quadraped;
         *   class Dog extends QuadrapedImpl;
         * </pre></p>
         * <p/>
         * <p>This method will look for the following config files for Dog:
         * <pre>
         *   Animal
         *   Animal-context
         *   AnimalImpl
         *   AnimalImpl-context
         *   Quadraped
         *   Quadraped-context
         *   QuadrapedImpl
         *   QuadrapedImpl-context
         *   Dog
         *   Dog-context
         * </pre></p>
         * <p/>
         * <p>Note that the validation rules for Thing is never looked for because no class in the
         * hierarchy directly implements Thing.</p>
         *
         * @param clazz     the Class to look up validators for.
         * @param context   the context to use when looking up validators.
         * @param checkFile true if the validation config file should be checked to see if it has been
         *                  updated.
         * @param checked   the set of previously checked class-contexts, null if none have been checked
         * @return a list of validator configs for the given class and context.
         */
        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;
        }
    
        private List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
            List<ValidatorConfig> retList = Collections.emptyList();
    
            URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
    
            if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
                InputStream is = null;
    
                try {
                    is = fileManager.loadFile(fileUrl);
    
                    if (is != null) {
                        retList = new ArrayList<ValidatorConfig>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
                    }
                } catch (Exception e) {
                    LOG.error("Caught exception while loading file " + fileName, e);
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            LOG.error("Unable to close input stream for " + fileName, e);
                        }
                    }
                }
    
                validatorFileCache.put(fileName, retList);
            } else {
                retList = validatorFileCache.get(fileName);
            }
    
            return retList;
        }
    }
    View Code

    (查看方式:续重validate方法,ctrl+T)。

    4、com.opensymphony.xwork2.validator.AnnotationActionValidatorManager该类中的方法:

     public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
            List<Validator> validators = getValidators(object.getClass(), context, method);
            Set<String> shortcircuitedFields = null;
    
            for (final Validator validator : validators) {
                try {
                    validator.setValidatorContext(validatorContext);
    
                   。。。
    
                    validator.validate(object);
                } finally {
                    validator.setValidatorContext(null);
                }
    
            }
        }

    中的validator.validate(object);代码,将会调用com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator.

    流程图:

     
    其中validation配置在xwrok-core.jar中的com.opensymphony.xwork2.validator.validators包下的default.xml文件中:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator Definition 1.0//EN"
            "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
    
    <!-- START SNIPPET: validators-default -->
    <validators>
        <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
        <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
        <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
        <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
        <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
        <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
        <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
        <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
        <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
        <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
        <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
        <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
        <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
        <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
        <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
        <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
    </validators>
    <!--  END SNIPPET: validators-default -->
     
     
  • 相关阅读:
    Code Forces Gym 100886J Sockets(二分)
    CSU 1092 Barricade
    CodeChef Mahesh and his lost array
    CodeChef Gcd Queries
    CodeChef GCD2
    CodeChef Sereja and LCM(矩阵快速幂)
    CodeChef Sereja and GCD
    CodeChef Little Elephant and Balance
    CodeChef Count Substrings
    hdu 4001 To Miss Our Children Time( sort + DP )
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/6685981.html
Copyright © 2011-2022 走看看