前言
    其实早在四月底就在JR上发表了这篇文章,这次再次搬出来一是为了资料集中,二是做一些修改和更详细的描述.和以往一样入门和介绍就不说了,如何学习在上篇文章Struts2+JFreeChart上有介绍.


正题
1.工程目录结构图:
Struts2 自定义验证器 _validator
2.以下依次帖代码:
    a).    web.xml
 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
    
<filter>
        
<filter-name>struts2</filter-name>
        
<filter-class>
            org.apache.struts2.dispatcher.FilterDispatcher
        
</filter-class>
    
</filter>
    
<filter-mapping>
        
<filter-name>struts2</filter-name>
        
<url-pattern>/*</url-pattern>
    
</filter-mapping>

    
<welcome-file-list>
        
<welcome-file>ValidatorDate.jsp</welcome-file>
    
</welcome-file-list>
</web-app>

    b).    struts.xml
 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC 
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 
    "http://struts.apache.org/dtds/struts-2.0.dtd"
>

<struts>
    
<include file="struts-validation.xml" />
</struts>

    c).    struts.properties
 

struts.ui.theme=simple

    d).    struts-validation.xml
 

<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd"
>

<struts>

    
<package name="validationExamples" extends="struts-default"
        namespace
="/validation">
        
<action name="ValidatorField"
            class
="com.tangjun.validator.ValidatorAction">
            
<!-- 这里input表示验证失败后指定跳转到什么地方去 -->
            
<result name="input" type="dispatcher">/ValidatorDate.jsp</result>
            
<result>/success.jsp</result>
        
</action>
    
</package>
</struts>

    e).    ValidatorAction-validation.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC 
          "-//OpenSymphony Group//XWork Validator 1.0.2//EN" 
          "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"
>
<validators>
    
<field name="regexYearField">
        
<field-validator type="mydate">
            
<param name="maxYear">2008</param>
            
<param name="minYear">1900</param>
            
<message>
                
<![CDATA[ 不是一个有效的年份! ]]>
            
</message>
        
</field-validator>
    
</field>
    
<field name="regexMonthField">
        
<field-validator type="mydate">
            
<message>
                
<![CDATA[ 不是一个有效的月份! ]]>
            
</message>
        
</field-validator>
    
</field>
    
<field name="regexDayField">
        
<field-validator type="mydate">
            
<!-- 是否使用正则表达式验证日期 -->
            
<param name="isRegex">false</param>
            
<!-- 使用自定义正则表达式验证日期 -->
            
<!-- 
                <param name="expression"></param>
            
-->
            
<message>
                
<![CDATA[ 当前月份的天数不对! ]]>
            
</message>
        
</field-validator>
    
</field>

</validators>

说明:验证文件需要和Action在同一目录下, 验证文件命名规则:验证文件xxxx-validation.xml,这个xxxx就是你前面action的类名字,xxx-xxx-validation.xml第二个xxx表示是别名.
    f).    validators.xml
 

<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="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="mydate" class="com.tangjun.validator.DateEx"/>
</validators>

说明:在源码com.opensymphony.xwork2.validator.ValidatorFactory中,在325行至330那一段代码可以看出,如果不能在根目录下找到validators.xml文件,验证框架将调用默认的验证设置,即com.opensymphony.xwork2.
validator.validators目录下default.xml里面的配置信息.最后一个<validator name="mydate" class="com.tangjun.validator.DateEx"/>就是我添加的默认验证器类型.
    g).    ValidatorAction

package com.tangjun.validator;

import com.opensymphony.xwork2.ActionSupport;

/**
 * 验证日期
 * 
 * 
@author lzl
 * 
 
*/
public class ValidatorAction extends ActionSupport {

    
private static final long serialVersionUID = -4829381083003175423L;

    
private Integer regexYearField;

    
private Integer regexMonthField;

    
private Integer regexDayField;

    
public ValidatorAction()
    {
        regexYearField 
= null;
        regexMonthField 
= null;
        regexDayField 
= null;
    }
    
    @Override
    
public String execute() throws Exception {
        
return SUCCESS;
    }

    
public Integer getRegexDayField() {
        
return regexDayField;
    }

    
public void setRegexDayField(Integer regexDayField) {
        
this.regexDayField = regexDayField;
    }

    
public Integer getRegexMonthField() {
        
return regexMonthField;
    }

    
public void setRegexMonthField(Integer regexMonthField) {
        
this.regexMonthField = regexMonthField;
    }

    
public Integer getRegexYearField() {
        
return regexYearField;
    }

    
public void setRegexYearField(Integer regexYearField) {
        
this.regexYearField = regexYearField;
    }

}

    h).    BeanUtils

package com.tangjun.validator;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BeanUtils {

    
/* 默认验证日期正则表达式 */
    
private static final String DateExpression = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)";

    
/**
     * 验证日期合法性(通过抛出错误方式来验证)
     * 
@param date
     * 
@return
     
*/
    
public static boolean validatorDate(String date) {
        
// 注意这个地方"yyyy-MM-dd"如果设置成了yyyy-mm-dd的话验证将失灵
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        
// 这个的功能是不让系统自动转换 不把1996-13-3 转换为1997-3-1
        sdf.setLenient(false);
        
try {
            sdf.parse(date);
            
return true;
        } 
catch (ParseException e) {
            
return false;
        }
    }

    
/**
     * 使用正则表达式判定日期
     * 
     * 
@param str
     * 
@param expression
     * 
@return
     
*/
    
public static boolean regexValidatorDate(String str, String expression) {
        
// 使用正则表达式进行判定
        if (expression == null || expression.trim().length() == 0)
            expression 
= BeanUtils.DateExpression;
        Pattern pattern;
        
// System.out.println(expression);
        pattern = Pattern.compile(expression, 2);
        Matcher matcher 
= pattern.matcher(str.trim());
        
if (!matcher.matches()) {
            
return false;
        }
        
return true;
    }
}

说明:验证日期的正则表达式就是在网上找的,可以替换成自己的.
    i).    DateEx
 

package com.tangjun.validator;

import java.util.HashMap;

import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;

public class DateEx extends FieldValidatorSupport {

    
private static HashMap<String, Integer> map = new HashMap<String, Integer>();

    
/* 默认最大年限 */
    
private static final Integer MAX_YEAR = 2008;

    
/* 默认最小年限 */
    
private static final Integer MIN_YEAR = 1900;

    
/* 最大年限 */
    
private Integer maxYear;

    
/* 最小年限 */
    
private Integer minYear;

    
/* 验证日期的正则表达式 */
    
private String expression;

    
/* 是否使用正则表达式验证 */
    
private Boolean isRegex;
    
    
public DateEx() {
        maxYear 
= null;
        minYear 
= null;
        expression 
= null;
        isRegex 
= false;
    }

    
public void validate(Object object) throws ValidationException {

        
// 获得字段的名字
        String fieldName = getFieldName();
        
// 获得输入界面输入的值
        String value = getFieldValue(fieldName, object).toString();

        
if (value == null || value.length() <= 0)
            
return;

        
if (fieldName.equals("regexYearField"))
            validateYear(value, object);
        
else if (fieldName.equals("regexMonthField"))
            validateMonth(value, object);
        
else if (fieldName.equals("regexDayField"))
            validateDay(value, object);
    }

    
/**
     * 验证年份
     * 
     * 
@param object
     * 
@throws ValidationException
     
*/
    
private void validateYear(String value, Object object)
            
throws ValidationException {
        
/* 设置默认值 */
        
if (maxYear == null) {
            maxYear 
= MAX_YEAR;
        }
        
if (minYear == null) {
            minYear 
= MIN_YEAR;
        }

        Integer temp 
= null;
        
try {
            temp 
= Integer.valueOf(value);
        } 
catch (NumberFormatException ex) {
            addFieldErrorEx(getFieldName(), object);
            
return;
        }

        
if (temp != null) {
            
if (temp >= minYear && temp <= maxYear)
                map.put(
"year", temp);
            
else
                addFieldErrorEx(getFieldName(), object);
        }
    }

    
/**
     * 验证月份
     * 
     * 
@param object
     * 
@throws ValidationException
     
*/
    
private void validateMonth(String value, Object object)
            
throws ValidationException {
        
try {
            Integer temp 
= Integer.valueOf(value);
            
if (temp != null) {
                
if (temp >= 1 && temp <= 12)
                    map.put(
"month", temp);
                
else
                    addFieldErrorEx(getFieldName(), object);
            }
        } 
catch (NumberFormatException ex) {
            addFieldErrorEx(getFieldName(), object);
            
return;
        }
    }

    
/**
     * 验证日期
     * 
     * 
@param object
     * 
@throws ValidationException
     
*/
    
private void validateDay(String value, Object object)
            
throws ValidationException {
        
try {
            Integer temp 
= Integer.valueOf(value);
            
if (temp != null) {
                Integer year 
= map.get("year");
                Integer month 
= map.get("month");
                
                
//直接使用天数
                if(year==null || month==null)
                {
                    
return;
                }
                
                
// 是否使用正则表达式验证
                if (!isRegex) {
                    
if(!BeanUtils.validatorDate(year + "-" + month + "-" + temp))
                        addFieldErrorEx(getFieldName(), object);
                } 
else {
                    String StrDay 
= temp < 10 ? "0" + temp.toString() : temp
                            .toString();

                    String StrMonth 
= month < 10 ? "0" + month.toString()
                            : month.toString();

                    
//前面已经验证过了
                    
//String StrYear = year < 100 ? year < 10 ? "200" + year: "19" + year : "1" + year;

                    String str 
= year + "-" + StrMonth + "-" + StrDay;

                    System.out.println(
"Date:" + str);

                    
if (expression == null) {
                        
if(!BeanUtils.regexValidatorDate(str, null))
                            addFieldErrorEx(getFieldName(), object);
                    } 
else {
                        
if (expression.trim().length() > 0) {
                            
if(BeanUtils.regexValidatorDate(str, expression))
                                addFieldErrorEx(getFieldName(), object);
                        }
                    }
                }
                map.clear();
            }
        } 
catch (NumberFormatException ex) {
            addFieldErrorEx(getFieldName(), object);
            
return;
        }
    }

    
/**
     * 控制是否只显示一条报错信息
     * 
     * 
@param arg0
     * 
@param arg1
     
*/
    
private void addFieldErrorEx(String arg0, Object arg1) {
        
//if (this.getValidatorContext().getFieldErrors().size() == 0)
            addFieldError(arg0, arg1);
    }

    
public Integer getMaxYear() {
        
return maxYear;
    }

    
public void setMaxYear(Integer maxYear) {
        
this.maxYear = maxYear;
    }

    
public Integer getMinYear() {
        
return minYear;
    }

    
public void setMinYear(Integer minYear) {
        
this.minYear = minYear;
    }

    
public String getExpression() {
        
return expression;
    }

    
public void setExpression(String expression) {
        
this.expression = expression;
    }

    
public Boolean getIsRegex() {
        
return isRegex;
    }

    
public void setIsRegex(Boolean isRegex) {
        
this.isRegex = isRegex;
    }
}

说明:在DateEx里面,这个时候你就可以参照如IntRangeFieldValidator(反编译源码)等他实现的验证器来写自己的验证器了。这里注意了,你在这个类里面每写一个属性(含get set方法),就对应的一个xxxx-validation.xml这个配置文件里面<param name="expression"></param>这个name的名字了,可以在扩展类里面直接获得了,根据传入参数进行自定义方式验证了。如下object是validate传进来的参数,表示你输入的数据对象:
// 获得字段的名字
String fieldName = getFieldName();
// 获得输入界面输入的值
String value = getFieldValue(fieldName, object).toString();

总结
验证器是拦截器即Interceptor实现的,所以并没有看见任何代码把他们和我的action文件关联起来,他们默认的验证了,这方面可以参考Max On Java的文章.