JSF通过定制标签与JSP集成。之前展示过的所有 JSF标签,<h:inputText>、<h:outputText>、<h:form> 和<f:view>等,都是定制标签。根据规范要求,JSF 实现必须通过提供访问所有标准组件、呈现器、验证器和转换器的定制标签来支持JSP。这些标签库(包括在JSF JAR中)列于表3-6中。
表3-6 JSF定制标签库
URI |
名 称 |
通用前缀 |
说 明 |
http://java.sun.com/jsf/core |
Core |
f |
包含独立于特定呈现器的标签(如<f:view>、<validator>,等等) |
http://java.sun.com/jsf/html |
HTML |
h |
包含所有标准组件和HTML呈现包 |
这些库中的所有标签都以某种特定的方式命名和实现。这样,基于JSP的应用就保证可以在不同的 JSF 实现之间移植。大多数IDE,包括在本书中提到的,都可用于JSP开发。
因为JSP是唯一的所有实现都要求的显示技术,我们在本书中都是用它来作为示例。如果你不使用JSP也没关系,我们讲述的大多数概念并未和某种显示技术紧密相关,你可以在附录A读到有关使用其他显示技术的信息。
对大多数问题,在JSP中使用JSF仅是使用JSF定制标签库的问题。但是,也还有一些你需要知道的要点,比如JSP包含。
3.2.1 使用JSP包含
JSP的关键特征之一是,能够将来自于多个JSP页面 中的内容集成到一个页面中。这通常用于完成包含页眉和页脚之类的工作。JSP 支持两类包含:动态的和静态的。动态包含(通过<jsp:include>标签或JSTL <c:import>标签实现)在运行时访问资源。这种情况下,控制被转发到所包含的JSP。从被包含的JSP返回的响应将与调用页面发回的 响应进行合并。修改了动态的被包含页面后,它们将在所有调用页面中自动更新。
静态包含在JSP转换——即在页面被转换成Java 代码并且被编译时,集成资源。原页面的内容基本上是被拷贝到调用页面中。对被包含页面的修改不能在调用页面时自动更新。因为它们已经有了自己的内容拷贝。 它们必须是能够被编辑的,以便它们在更新内容时被重新编译(JSP 2.0的隐含包含,可以在web.xml中进行配置,其处理也类似于静态包含)。
JSF支持两种包含。对动态包含,有两个要求:
l 被包含页面必须封装在JSF <f:subview> 核心标签中。这个标签可以位于被包含页面中,也可以围绕包含语句;
l 被包含页面中的所有模板文本和非JSF标签必须位于JSF <f:verbatim> 核心标签之内。
所以,假定有下面的JSP页面的代码片断:
而Foo.jsp可能是这样:
可以看到,整个被包含页面被封装 在<f:subview>标签中,并且所有的非JSF 标签和模板文本封装在<f:verbatim> 标签中。另外,也可以将<f:subview> 标签移到第一个页面,围绕在<jsp:include> 标签之外。
使用静态包含要更简单些。无特别的限制——甚至并不非要使用<f:subview> 标签。
在上例中,我们展示了一个假定的定制标签,<customtag:dothis>,它可能执行任何任务。这样就强调一个重点:可以和其他JSP定制标签一起使用 JSF。
3.2.2 与JSTL以及其他JSP定制标签一起使用JSF
这些有关JSF定制标签库的讨论都很不错,但是如果你 希望有自己的定制标签,或者使用第三方的标签又如何呢?而如果你正在使用JSP 标准标签库(JSTL),这是能够优雅地完成刚刚所述的所有事情的一套标准标签,又该如何?多数情况下,可以将它们与你的JSF标签混合使用。Faces 标签可以嵌套在其他标签中。而对于某些产品,如IBM的WebSphere Application Developer [IBM,WSAD],鼓励使用这种方法,而其他产品,如Sun的 Java Creator Studio [Sun,Creator],则鼓励使用纯粹的JSF方法。Oracle的 JDeveloper [Oracle,JDeveloper],允许你混合使用,但是也鼓励你使用纯粹的JSF方式。
注解 无论何时将JSF标签嵌套到非JSF标签中,一定要为它指定一个组件标识符(见前一章关于组件标识符的更多信息)。
因为JSTL是标准,而且很多人对它都很熟悉,我们将用它来演示与定制标签一起使用JSF(如果要获得JSTL的详细信息,参考Shawn Bayern的一本非常棒的书JSTL in Action [Bayern])。从一个简单的例子开始(如代码清单3-2所示)。该示例混合使用了一些JSTL 标签和JSF 标签。这段代码同时导入了JSF 标签库和核心JSTL 标签库。
代码清单3-2 混合JSTL标签和JSF标签
在这个例子中,JSTL 和JSF 标签都嵌套在JSF <f:view> 标签中,而该标签定义了JSF组件树的开始。例子使用了JSF HtmlOutputText 组件(<h:outputText>)和JSTL <c:out> 标签来显示文本。JSTL <c:import> 标签将系统的web.xml文件包含到页面中(实际上你一般是不可能想要与他人分享这个文件的,因此不要在实际应用中这样做)。因为web.xml 是一个XML 文件,<c:import> 标签被嵌套在<f:verbatim>标签中,后者是JSF UIOutput 组件,其呈现器转义了XML以便它在HTML页面中正常显示。这个例子并没做什么大不了的事情,但是它演示了在同一个页面中混合使用不同标签的能力。
注意,我们嵌套了JSTL标签到JSF <f:verbatim> 标签中。通常,嵌套JSF标签到其他标签之中是很容易的。事实上,任何可以显示其子组件的组件,比如HtmlDataTable 和 HtmlPanelGrid,都要求模板文本和被嵌套的标签位于<f:verbatim>标签中(<f:verbatim> 标签在第4章讨论)。
随同JSF使用JSTL最大的好处在于,两者使用 相似的表达式语言来引用对象(这对于JSP 2.0的表达式语言也是如此)。这样使你可以很容易地以一种直观的方式在JSTL和JSF标签之间共享数据。为了演示这个特点,我们来看另一个例子,可以 使用户在HtmlInputText 控件中输入数值,然后使用该数值通过JSTL <c:forEach> 标签来重复显示字符串。代码示于代码清单3-3中。
代码清单3-3 对同一个后台bean混合使用JSF 和JSTL 标签
警 告 如果和受管bean一起使用JSP或者 JSTL表达式,你需要确保被引用的bean已被创建,可以通过JSF表达式、Java代码或者你自己的定制标签来实现。这是因为这些老的表达式语言并不 知道JSF的受管bean创建工具(参考3.3节关于创建受管bean的信息)。
这 段代码引用了一个称为exampleBean的JavaBean,它有个int类型的number 属性。使用了HtmlInputText组件来基于用户输入更新bean的属性值。用户点击Go!按钮(HtmlCommandButton组件) 时,number属性被更新,且页面被重新显示。这样,JSTL <c:forEach>标签将使用<c:out>标签重复显示文本exampleBean.number 次。<c:forEach> 标签仅在exampleBean.number大于0时执行;这是由JSTL <c:if> 标签控制的。
你不能在一个迭代其自身body的标签中使用JSF组件标签,如JSTL <c:forEach>标签之中使用JSF标签。推荐方法是使用HtmlDataTable 组件或者其他组件来在数据集或者集合之上进行迭代。
这个例子中,并没有JSF 组件嵌套在JSTL <c:if>标签中。但是如果组件被显示一次后,然后在页面重新显示时又被诸如<c:if>之类的条件标签隐藏,会怎样?在组件 第一次被显示时,它被添加到视图中。第二次,如果<c:if>标签没有显示这个组件,JSF将把它从视图中删除。这意味着某些输入控件将丢失 其本地值,而你就不能再引用这些组件(通过客户端标识符引用或者在代码中引用)。我们来看另一个例子,如代码清单3-4所示,它来自于代码清单3-3的同 一个页面。
JSTL <c:if> 标签在exampleBean.number 大于10时,将执行其body部分。如果body部分被执行,那么所有嵌套的组件将被添加到视图中,并显示出来。反之,这些组件将被删除(如果它们先前已 被添加了)。这种情况如图3-4所示。而图3-5显示的是代码清单3-3和3-4所用的页面的输出。
图3-4 假设你通过JSTL条件标签(或者其他定制标签)来控制组件的可视性,如果某个组件未被显示,它将被从视图中删除。这意味着组件也会忘记其本地值
代码清单3-4 通过JSTL有条件地显示JSF组件
你也可以将这些组件放在HtmlPanelGroup中并且设置其rendered 属性等于同一个表达式,以达到与代码清单3-4相同的效果。HtmlPanelGroup被用作多个组件的容器。如下例:
图3-5 显示于代码清单3-3和3-4中的JSP页面的输出。顶部的输入字段(HtmlInputText组件)的值被关联到 exampleBean.number后台bean属性,而该值被JSTL <c:forEach> 标签用于重复显示字符串exampleBean.number次。在页面的底部,如果exampleBean.number大于10,JSTL <c:if>标签将使用JSF 组件显示一个表单。否则,组件将不会被显示,并且会被从视图中删除(并且输入控件也将丢失其值)
如果exampleBean.number大于10,面板变为可见。这时,如果组件没被显示也将不被删除。这是个不使用JSTL而使用纯粹的JSF的好例子。
提 示 即使定制标签,像JSTL提供的那些,可以提供很多功能,但如果你正在从头开发(或者重构),应该首先看看是否可以使用标准JSF组件来实现所需要的功 能。使用优秀的组件和良好设计的后台bean,可以避免在页面中使用太多的JSTL 标签。你可以使用JSF标准组件来隐藏和显示整个面板,或者完成各种强大的任务。
下面是几个随JSTL国际化和格式化标签一起使用JSF标签的互操作性约束:
l 不推荐使用<fmt:parseDate> 和 <fmt:parseNumber>标签。应该使用带有DateTime 或者 Number 转换器的(都在第6章讨论)HtmlInputText 组件(第5章讨论);
l 不应该使用用来决定或者指定页面的字符集编码的<fmt:requestEncoding> 标签。通常,JSF 将自动处理这些,而如果需要强制指定特殊的编码,应该使用JSP 页面指令:<%page contentType="[content-type];[charset]"%>;
l 也不应该使用<fmt:setLocale>标签。因为它不支持JSF,这可能会导致JSTL标签使用一个场所而JSF 组件使用另一个场所,这将是一场灾难。相反,你应该使用JSF的国际化特征(在第6章讨论)。为了控制特定页面的场所,使用UIViewRoot 组件的locale属性,这将在第4章讨论。JSF的国际化特征支持JSF和JSTL。
结合使用JSF 和JSTL的功能将是十分强大的。你自己开发的或者从第三方获得的定制标签都应该能够可以很好的与我们这些所述的JSF和JSTL标签一起工作。通常,你应该尽可能的坚持使用JSF 标签。