日期:2014-05-16  浏览次数:20495 次

编写JSF用户自定义UI组件(之四)

【续前。。。。。。】

八、步骤二:编写标签处理器类

UI组件类不同于普通Java类的地方是,它不是在Java代码中被引用,而是在JSP文件中。在JSP文件中,我们不能通过new操作符实例化UI组件类,只能通过JSP标签引用和实例化UI组件类。

前文介绍过如何引用一号组件,即在JSP文件中写入类似这样的标签即可:

<x:repeatText text="Hello world!" number="3">


当 JSP引擎读到这个标签时,它会把x:repeatText翻译为对某个类的调用,这个类就是所谓的标签处理器类tag handler。JSP引擎会将组件类的一个实例传递给标签处理器类,标签处理器类的功能是给这个组件类实例的各个属性赋值,值的来源就是标签中的 text="Hello world!" number="3"。

可见,JSP引擎和标签处理器类配合工作,可以实现类似于Java代码 中实例化一个类并给类的属性赋值的功能。我们在JSP文件中写下<x:repeatText text="Hello world!" number="3">,实际上就相当于实例化一号组件类并给其text和number分别赋值“Hello world!”和“3”。图 7示出了JSP标签和普通Java代码的类比。


?
图 7 JSP标签和普通Java代码的类比

?

也许你已经留意到图 7中标签名repeatText和组件类名StrRepeat之间的不同,它们是如何对应的,我们将在后文解释。

理解了标签处理器类的作用后,我们就很容易编写它的代码了。如同组件类StrRepeat可以继承UIComponentBase类一样,标签处理器类也可 以继承JSF框架提供的几个标签处理父类,以减少代码量。例如,一号组件的标签处理类可以继承UIComponentELTag类,如果我们将一号组件的 标签处理器类命名为StrRepeatTag,则其声明为:

public class StrRepeatTag extends UIComponentELTag{
    ...
}
?

标签处理器类的属性决定了我们允许用户在JSP文件中为自定义UI组件的哪些属性赋值。对于一号组件,我们允许用户在JSP标签中对一号组件的text和 number属性赋值,因此,StrRepeatTag类也必须有text和number这两个属性,用来接收用户在JSP标签中写入的值,然后转赋给一 号组件。这很容易,只需为StrRepeatTag类定义两个标准的JavaBeans属性即可,如程序清单 4所示。

程序清单 4 StrRepeatTag类的text和number属性

    private String text;
    public String getText() { return text; }
    public void setText(String text) { this.text = text; }

    private int number;
    public int getNumber() { return number; }
    public void setNumber(int number) { this.number = number; }
?

读者不难发现,这部分代码和一号组件类StrRepeat的(见程序清单 1)完全一样,甚至连属性名称都相同。当然,这不是强制要求,标签处理器类和相应的组件类的属性可以不同名,个数也可以不同,如果你无需允许用户在JSP标签中给组件类的所有属性赋值。

标签处理器类有几个方法,我们逐一说明:

1.??? 返回组件类型的方法getComponentType()(参见程序清单 5)

程序清单 5 标签处理器类StrRepeatTag的getComponentType()方法代码

    @Override
    public String getComponentType() {
        return "MyComponentType";
    }

?
这个方法返回一个称被为“组件类型”的字符串。组件类型决定了标签处理器类和组件类之间的对应关系,例如,当JSP引擎碰 到<x:repeatText.../>这样的标签时,之所以能够正确地判断出这是与StrRepeat组件类相对应的一个标签,正是由于“ 组件类型”字符串起着牵线搭桥的作用。具体的对应方法,后文有更详细的说明。

父类UIComponentELTag中的getComponentType()方法是抽象的,因此必须在子类中实现它。本例中,我们返回的组件类型为MyComponentType,如程序清单 5所示。

2.??? 返回呈现器类型的方法getRendererType()(参见程序清单 6)

程序清单 6标签处理器类StrRepeatTag的getRendererType()方法代码

    @Override
    public String getRendererType() {
        return null;
        //throw new UnsupportedOperationException("Not supported yet.");
    }
?

呈现器是一种专门负责向http响应输出组件内容的类。JSF允许组件类自己输出自己的内容,也允许它假手于人,即交给与之对应的呈现器类来输出。这个方法 返回的呈现器类型,正是用来供JSF框架选择合适的呈现器类的。由于一号组件是自己负责输出,不需要呈现器类,因此,我们让这个方法简单地返回一个 null,即告诉JSF框架,一号组件没有对应的呈现器类。

父类UIComponentELTag中的getRendererType()方法是抽象的,因此必须在子类中实现它。

3.??? 给组件类实例赋值的方法setProperties()

程序清单 7标签处理器类StrRepeatTag的setProperties()方法代码

    @Override
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        component.getAttributes().put("text", this.getText());
        component.getAttributes().put("number", this.getNumber());
    }
?

我们已在前面提到,用户可以通过JSP标签给组件类实例的属性赋值,setProperties()方