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 -->
     
     
  • 相关阅读:
    【leetcode】下一个排列
    【leetcode】配对交换
    【leetcode】两个相同字符之间的最长子字符串
    052-126&127
    052-125
    052-124
    052-123
    052-122
    052-121
    052-120
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/6685981.html
Copyright © 2011-2022 走看看