简化JSF 2.0页面制作
JavaServer Faces技术提供了一个服务端组件框架,简化了Java EE应用程序用户界面的开发,其中最显著的改进是页面制作,通过使用标准的JavaServer Faces视图声明语言(JavaServer Faces View Declaration Language,俗称Facelets)创建一个JSF页面更加容易。
Facelets
Facelets是一个强大的轻量级声明语言,可以使用它展示一个JSF页面,使用Facelets时,你可以使用HTML风格的模板展示一个JSF页面,也可以构建一个组件树,JSF应用程序中的用户界面通常是由JSF组件构成的JSF页面,Facelets在JSP之上提供了更多优点。
在JSP中,Web页面中的元素是按照渐进顺序处理和渲染的,而JSF提供了它自己的处理和渲染顺序,这可能会导致不可预测的行为发生,Facelets解决了这个问题,通过模板,Facelets也允许代码复用,可以大大减少开发UI的时间,现在Facelets已经成为构建JSF应用程序的首选技术。
Facelets通常是使用XHTML标记语言编写的,因此Facelets是可以跨不同开发平台的,下面是Java EE 6教材中提供的JSF页面的Facelets XHTML代码部分:
- <xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
- xmlns:f="http://java.sun.com/jsf/core"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:ui="http://java.sun.com/jsf/facelets">
- <head>
- <title>Guess Number JSF Application</title>
- </head>
- <body>
- <h:form>
- <h2>
- Hi. My name is Duke. I am thinking of a number from <b>
- <h:outputText value="#{UserNumberBean.minimum}"/> to
- <b>
- <h:outputText value="#{UserNumberBean.maximum}"/>.
- <p>
- Can you guess it ?
- </p>
- <h:graphicImage id="waveImg" url="/wave.med.gif" />
- <h:inputText id="userNo"
- value="#{UserNumberBean.userNumber}">
- converterMessage="#{ErrMsg.userNoConvert}">
- <f:validateLongRange
- minimum="#{UserNumberBean.minimum}"
- maximum="#{UserNumberBean.maximum}"/>
- </h:inputText>
- <h:commandButton id="submit"
- action="success" value="submit" />
- <h:message showSummary="true" showDetail="false"
- style="color: red;
- font-family: 'New Century Schoolbook', serif;
- font-style: oblique;
- text-decoration: overline"
- id="errors1"
- for="userNo"/>
- </h2>
- </h:form>
- </body>
- </html>
页面渲染效果如图1所示。
这个Facelets XHTML页面和普通JSP页面并没有多大不同,Facelets支持JSF和JSTL标签库,它也包括一个Facelets标签库,支持功能丰富的页面模板。命名空间声明xmlns:ui="http://java.sun.com/jsf/facelets"就是针对facelets标签库的,但这里没有使用facelets标签库的标签,facelets也支持统一的表达式语言。
模板
使用模板,你可以创建一个页面作为应用程序中其它页面的模板,这样可以避免多次创建结构类似的页面,同时也可以统一应用程序中多个页面的视觉风格。
Facelets标签库包括一个模板标签<ui:insert>,为了实施模板化,首先创建一个包括<ui:insert>标签的模板页面,然后创建一个使用这个模板的客户端页面,在客户端页面中,使用<ui:composition>标签指定模板,使用<ui:define>标签指定插入到模板中的内容。
下面是一个模板页面的内容:
- <xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:h="http://java.sun.com/jsf/html"
- <head>
- <title><ui:insert name="title">Page Title</ui:insert</title><body>
- </head>
- <body>
- <div>
- <ui:insert name="Links"/>
- </div>
- <div>
- <ui:insert name="Data"/>
- </div>
- </body>
- </html>
下面是使用这个模板的客户端页面代码:
- <xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:h="http://java.sun.com/jsf/html"
- <body>
- <ui:composition template="/template.xhtml">
- This text will not be displayed.
- <ui:define name="title">
- Welcome page
- </ui:define>
- <ui:define name="Links">
- ... [Links should be here]
- </ui:define>
- <ui:define name="Links">
- ... [Data should be here]
- </ui:define>
- </ui:composition>
- This text also will not be displayed.
- </body>
- </html>
当客户端调用这个模板时,它使用标题Welcome Page渲染这个页面,这个页面显示了两部分内容,一个显示客户端中指定的链接列表,另一个显示客户端中指定的数据。
混合组件
混合组件时JSF中的一个新特性,通过它创建自定义JSF组件会更加容易。你可以使用JSF页面标记和其它JSF组件创建混合组件。在Facelets的标注下,任何XHTML页面都可以变成一个混合组件。此外,混合组件可以有验证器,转换器和监听器。
创建好混合组件后,你可以将它保存到库中,以后有需要时就可以调用了。
让我们创建一个渲染为登录面板的混合组件,用户登录时,组件反馈一个登录事件,如图2所示。
下面是混合组件的源代码:
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:f="http://java.sun.com/jsf/core">
- xmlns:f="http://java.sun.com/jsf/facelets">
- xmlns:composite="http://java.sun.com/jsf/composite">
- <h:head>
- <title>This content will not be displayed in the rendered output</title>
- </h:head>
- <h:body>
- <composite:interface>
- <composite:actionSource name="loginEvent"/>
- </composite:interface>
- <composite:implementation>
- <table>
- <tr>
- <td>Username: <h:inputText id="username" /> </td>
- </tr>
- <tr>
- <td>Password: <h:inputSecret id="password" /></td>
- </tr>
- <tr>
- <td><h:commandButton value="Login" id="loginEvent" /></td>
- </tr>
- </table>
- </composite:implementation>
- </h:body>
- </html>
xmlns:composite="http://java.sun.com/jsf/composite"声明了混合UI组件的命名空间,<composite:interface>标签声明混合组件的使用契约,<composite:attribute>标签在使用契约中指定<composite:actionSource>标签,这个表示组件可以暴露一个事件,让使用这个混合组件的页面可以轻松访问它。
<composite:implementation>标签定义了混合组件的实现,这里的实现是一个简单的表,它包括用户名、密码和登录按钮JSF组件。
为了让混合组件可用,将代码保存为XHTML文件,将文件放到应用程序根目录下resources目录的子目录中即可。子目录的名字可以采用包含混合组件的资源库名字,JSF运行时通过向混合组件的标签名后追加.xhtml后缀查找混合组件。例如,如果你将标签命名为loginPanel,那么保存为混合组件的文件名就是loginPanel.xhtml。然后你就可以在Web页面中使用混合组件了,下面就是一个使用混合组件的Web页面代码示例:
- <!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:f="http://java.sun.com/jsf/core"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:ez="http://java.sun.com/jsf/composite/ezcomp">
- <head>
- <title>Example 01>/title>
- <style type="text/css">
- .grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9; }
- </style>
- </h:head>
- <h:body>
- <p>Usage of Login Panel Component</p>
- <ui:debug hotkey="p" rendered="true"/>
- <h:form>
- <div id="compositeComponent" class="grayBox" style="border: 1px solid #090;">
- <ez:loginPanel>
- <f:actionListener for="loginEvent" type="example01.LoginActionListener" />
- </ez:loginPanel>
- </div>
- <p><h:commandButton value="reload" /></p>
- <p><h:outputText value="#{loginActionMessage}" /></p>
- </h:form>
- </h:body>
- </html>
注意声明xmlns:ez="http://java.sun.com/jsf/composite/ezcomp",它指定了混合组件的命名空间和前缀,这里的ezcomp是资源目录的子目录名,JSF使用下面的约定:所有命名空间URI都以http://java.sun.com/jsf/composite/开头,使用资源库的名称结束。
<f:actionListener>
标签关联混合组件的行为监听器,标签中的for属性表示这个监听器是为混合组件上名为loginEvent行为事件准备的,你需要编写代码来处理事件,例如:
- import javax.faces.component.UIComponent;
- import javax.faces.component.ValueHolder;
- import javax.faces.context.FacesContext;
- import javax.faces.event.AbortProcessingException;
- import javax.faces.event.ActionEvent;
- import javax.faces.event.ActionListener;
- public class LoginActionListener implements ActionListener {
- public void processAction(ActionEvent event) throws AbortProcessingException {
- FacesContext context = FacesContext.getCurrentInstance();
- context.getExternalContext().getRequestMap().put("loginActionMessage",
- "Login event happened");
- }
- }
JSF 2.0对Ajax的支持
JSF 2.0天生就支持Ajax,利用Ajax技术,Web应用程序在后台以异步的方式从服务器获取数据。支持Ajax后,允许页面局部刷新,允许选择视图中的一个组件进行处理而不影响其它组件。
要在JSF中使用Ajax,需要访问有资源标识符的JavaScript资源jsf.js,它存在于javax.faces资源库中,包含让JSF与Ajax交互的JavaScript API,JavaScript API由一组标准的JavaScript函数组成,使JavaServer Faces框架中的Ajax操作变得简单了,你几乎不用直接包括这个文件,当你使用任何开启Ajax的标签或组件时,JSF会自动包括它。然后你就可以使用<f:ajax>标签或调用JavaScript API中的函数了。
下面是一个使用<f:ajax>标签的示例:
- <h:commandButton id="button1">
- <f:ajax execute="..." render="..."/>
- </h:commandButton>
这里的<f:ajax>标签是嵌套在<h:commandButton>标签内的,这样会结合在execute属性中指定的Ajax行为和<h:commandButton>标签呈现的命令按钮,你也可以指定一个event属性来识别JavaScript DOM事件,如果你不指定event属性,JSF使用组件的默认行为,这里的默认行为是onclick,因此JSF结合execute属性中指定的Ajax请求和呈现按钮的onclick事件。用户点击该按钮时,JSF提交Ajax请求给服务器。
使用<f:ajax>标签的一个好处是不用在页面中指定载入jsf.js,它会自动为你载入,相比之下,如果你调用JavaScript API,首先需要使用<h:outputScript>让jsf.js对当前视图可用,例如:
- <f:view contentType="text/html"/>
- <h:head>
- <meta...
- <title...
- </h:head>
- <h:body>
- ...
- <h:outputScript name="jsf.js" library="javax.faces" target="body"/>
- ...
- </h:body>
- ...
然后才可以使用JavaScript API中的函数产生Ajax请求。例如,你使用JavaScript函数jsf.ajax.request向服务器发送一个请求,如下面的代码:
- <h:commandButton id="button1" value="submit">
- onclick="jsf.ajax.request(this,event);" />
代码包括一个<h:commandButton>标签,它呈现为一个按钮,用户点击这个按钮时,向服务器提交一个Ajax请求。
Servlet 3.0和JSF2.0中的更多新特性
Servlet 3.0中另一个新特性是允许你使用ServletContext类中的方法通过编程在Web应用程序启动时向其添加Servlet和Servlet过滤器,使用addServlet()方法添加Servlet,使用addFilter()添加Servlet过滤器。结合可插拔式共享框架特性,Web框架可以在无开发人员介入的情况下实现自我配置。
此外Servlet 3.0加入了许多安全特性,除了声明安全外,Server 3.0通过HttpServletRequest接口提供了编程安全,例如,你可以在应用程序中使用HttpServletRequest的authenticate()方法执行用户名和密码的收集,或者使用login()方法指向容器验证一个非强制请求上下文中的请求调用者。有关Servlet 3.0的更多特性,请参阅JSR 315规范。JSF 2.0中的一些额外增强与资源如何打包和处理有关,JSF 2.0标准化了打包哪里的资源。所有资源都放在resources目录或一个子目录下,资源需要按顺序正确地进行渲染,例如CSS文件和JavaScript文件,图3显示了Netbeans中的一个JSF项目部分结构及文件,注意其中的resources目录,CSS和images目录。
JSF 2.0也包括显示和处理资源的API,使用javax.faces.application.Resource类显示一个资源,使用javax.faces.application.ResourceHandler类创建资源的实例。有关JSF 2.0的更多信息,请参阅JSR 314规范。