如果标准的验证器不能实现你所需要的验证,你可以很容易的创建一个自定义的验证器来验证用户的输入。有两种方法来实现验证代码:
利用后台bean方法来实现验证;提供一个实现Validator接口的类来验证。
这里,我们来看如何实现一个验证器接口。
实现验证器接口
验证器实例:
<bookstore:formatValidator
formatPatterns="9999999999999999|9999 9999 9999 9999|9999-9999-9999-9999"/>
一个验证器实现必须包含一个构造函数,一组可以访问标签所有属性的方法,一个validate方法,它重写Validator接口的validate方法。
FormatValidator类也定义了访问方法来设置formatPatterns属性,该属性指定输入框中可以接受的数据格式。此外,该类重写了Validator接口的validate方法,该方法验证了文本框而且当验证失败时显示自定义的错误信息。
Validate方法才是真正用于验证数据的,它需要三个参数:FaceContext实例,需要验证的组件,需要验证的数据。验证器只能验证实现了EditableValueHolder的组件中的数据。
下面是FormatValidator中的validate方法:


这个方法获取组件的本地值并把它转换成字符串。然后它遍历formatPatternsList,formatPatternsList包含了在formatPatterns属性中指定的格式串。在遍历过程中,这个方法将对照格式串检查组件的本地值,如果不符合格式,该方法将产生一个message错误消息,然后传递给ValidatorException构造器。实际上,这个message被传送到消息队列中,在呈现响应阶段显示给页面。错误消息也可以被MessageFactory从应用程序实例中检索到,所以如果要创建了它自定义的消息,应用程序必须提供一个MessageFactory类。当创建自己的应用时,你可以简单地拷贝MessageFactory类到你的应用。MessageFactory类的getMessage(FacesContext,String,Object)方法中,String 代表属性文件中的关键字,Object是格式串代表的对象,那个关键字对应FormatValidator类中的消息ID域。

当错误消息显示时,格式串将代替{0}:

JSF应用能在客户端和服务器上保存验证器和组件的状态。如果要把状态保存在客户端,你必须使Validator实现StateHolder接口,此外,还要实现其中的saveState(FaceContext)和restoreState(FacesContext,Object)方法,提供这些方法使验证器告诉JSF将属性通过请求来存储。
为了保存一系列值,你必须实现saveState(FaceContext)方法,该方法在呈现响应时调用,其间,响应状态通过进行一系列请求来保存状态,在实现saveState(FaceContext)方法时,你需要创建一个object数组,将属性值保存在该数组中,代码如下:

为了在返回中恢复状态值,验证器必须实现restoreState(FacesContext,Object)方法,Object参数代表Object数组中的Object对象,代码如下:

由于验证器实现了StateHolder接口,它必须重写isTransient和setTransient(boolean)方法,默认情况下,transientValue为false,这意味这验证器可以将状态值保存和恢复,代码如下:


创建自定义标签
如果是实现了Validator接口的验证器,你可以创建一个自定义验证器标签,该标签能带属性。为了创建一个自定义标签,你需要做两件事情:
1. 写一个标签处理器来创建和注册验证器类
2. 写一个TLD文件来定义标签及其属性
写一个标签处理器
和验证器有关的标签处理器必须继承ValidatorELTag类,它是所有自定义标签处理器的基类,该类用于创建验证器实例和注册验证器到组件。如:FormatValidatorTag类注册FormatValidator实例到组件
FormatValidatorTag标签处理器类功能有:
1. 设置验证器的ID;
2. 提供标签中定义的每个属性的一组访问方法;
3. 实现ValidatorELTag的createValidator方法,该方法创建一个验证器实例,然后设置验证器能接受的值的范围。
FormatValidatorTag标签的formatPatterns属性支持字面值和值表达式,因此,FormatValidatorTag的属性访问方法必须接受和返回一个ValueExpression实例。

最后,vreateValidator方法创建一个FormatValidator实例,从formatPatterns属性的值表达式中提取出值,然后设置formatPatterns属性给这个值。

写一个标记库描述符
为了定义一个标签,你要在标记库描述文件TLD中声明它,该文件是个XML文档,一个TLD文件包含关于库的信息和其中的每个标签。


Name元素定义了将在页面中使用的标记的名字,tag-class定义了标签处理器类,attribute定义了标签的每个属性。Deferred-value表明formatPatterns属性可以接收值表达式,type表明,表达式将被解析成一个String类型。