// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= JavaServer Faces 2.x 简介
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: JavaServer Faces 2.x 简介 - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, JavaServer Faces 2.x 简介

JavaServer Faces (JSF) 是 Java Web 应用程序的一个用户界面 (UI) 框架。设计 JSF 的目的在于，极大地缓解在 Java 应用服务器上运行的应用程序的编写和维护的压力，并将这些应用程序的 UI 重新呈现给目标客户端。JSF 具有如下易用性的优点：

* 可以轻松地通过一组可重用的 UI 组件来构造一个 UI
* 简化了应用程序数据与 UI 之间进行迁移的过程
* 有助于管理服务器请求中的 UI 状态
* 提供了将客户端生成的事件连接到服务器端应用程序代码的简单模型
* 可以轻松地构建和重用定制的 UI 组件

有关 JSF 框架的深入描述，请参见 link:http://docs.oracle.com/javaee/7/tutorial/doc/jsf-develop.htm[+Java EE 7 教程的第 12 章：使用 JavaServer Faces 技术进行开发+]。

本教程将对如何使用 NetBeans IDE 为 Web 应用程序提供 JSF 2.x 支持进行说明。首先为基本的 Web 应用程序添加 JSF 2.x 框架支持，然后继续执行以下任务：

* 创建一个 JSF 受管 Bean 来处理请求数据，
* 将受管 Bean 连接到应用程序的 Web 页，然后
* 将 Web 页转换为 Facelets 模板文件。

NetBeans IDE 为 JavaServer Faces 提供了长期支持。自 JSF 2.0 和 Java EE 6 版本起，NetBeans IDE 为 JSF 2.0 和 JSF 2.1 提供了支持。有关详细信息，请参见 link:jsf20-support.html[+NetBeans IDE 中的 JSF 2.x 支持+]。



image::images/netbeans-stamp-80-74-73.png[title="此页上的内容适用于 NetBeans IDE 7.2、7.3、7.4 和 8.0"]


要学完本教程，您需要具备以下软件和资源。

|===
|软件或资源 |要求的版本 

|link:https://netbeans.org/downloads/index.html[+NetBeans IDE+] |7.2、7.3、7.4、8.0、Java EE 包 

|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java 开发工具包 (JDK)+] |7 或 8 

|link:http://glassfish.dev.java.net/[+GlassFish Server+] |Open Source Edition 3.x 或 4 

|link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FjsfDemo.zip[+`jsfDemo` Web 应用程序项目+] |N/A 
|===

*注：*

* NetBeans IDE Java 包还包括本教程所需的 GlassFish Server（一种 Java EE 兼容的服务器）。
* 要将您的项目与工作解决方案进行比较，请下载link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FjsfDemoCompleted.zip[+完成的样例项目+]。



[[support]]
== 将 JSF 2.x 支持添加到 Web 应用程序

首先，在 IDE 中打开 <<requiredSoftware,`jsfDemo` Web 应用程序项目>>。在 IDE 中打开该项目后，可以使用该项目的 "Properties"（属性）窗口将框架支持添加到该项目中。

使用 IDE 还可以创建具有 JSF 2.x 支持的新项目。有关详细信息，请参见link:jsf20-support.html#creatingSupport[+创建具有 JSF 2.x 支持的新项目+]。

1. 单击 IDE 主工具栏中的 "Open Project"（打开项目）(image:images/open-project-btn.png[]) 按钮，或者按 Ctrl-Shift-O（在 Mac 上按 ⌘-Shift-O）。
2. 在 "Open Project"（打开项目）对话框中，导航到计算机上存储已解压缩教程项目的位置。选择该项目，然后单击 "Open Project"（打开项目）以在 IDE 中打开。

*注：*如果在安装 IDE 时未安装 JUnit 插件，当打开 NetBeans 项目时，系统可能会提示您解析到 JUnit 库的引用。



. 运行该项目以查看其在浏览器中的外观。在 "Projects"（项目）窗口中右键单击 `jsfDemo` 项目节点并选择 "Run"（运行），或单击主工具栏中的 "Run Project"（运行项目）(image:images/run-project-btn.png[]) 按钮。该项目将打包并部署到 GlassFish Server，并且您的浏览器随即打开以显示欢迎页 (`index.xhtml`)。

image::images/run-project.png[title="运行项目以在浏览器中查看它"]


. 单击 "Submit" 按钮。响应页 (`response.xhtml`) 显示如下：

image::images/response.png[title="欢迎页和响应页当前为静态页"]

目前欢迎页和响应页为静态页面，另外还有 `stylesheet.css` 文件和 `duke.png` 图像，这些都是通过浏览器访问的仅有的应用程序文件。



. 在 "Projects"（项目）窗口（Ctrl-1 组合键；在 Mac 上为 ⌘-1 组合键）中，右键单击项目节点，然后选择 "Properties"（属性）打开 "Project Properties"（项目属性）窗口。


. 选择 "Frameworks"（框架）类别，然后单击 "Add"（添加）按钮。


. 在 "Add a Framework"（添加框架）对话框中选择 "JavaServer Faces"。单击 "OK"（确定）。

image::images/add-framework.png[title="将 JSF 支持添加到现有项目"]

在选择了 JavaServer Faces 之后，各种配置选项就变为可用选项了。在 "Libraries"（库）标签下面，可以指定项目访问 JSF 2.x 库的方式。提供的 JSF 版本将取决于 IDE 和 GlassFish Server 的版本。默认选项是使用服务器 (GlassFish Server) 附带的库。但是，IDE 还捆绑了 JSF 2.x 库。（如果希望您的项目使用这些库，可以选择 "Registered Libraries"（已注册的库）选项。）

image::images/libraries-tab.png[title="指定对 JSF 2.x 库的访问权限"]


. 单击 "Configuration"（配置）标签。可以指定 Faces Servlet 在项目的部署描述符中注册的方式。还可以指定是否需要在项目中使用 Facelets 或 JSP 页。 

image::images/jsf-configuration.png[title="指定 Faces servlet 选项和首选语言"]

您还可以轻松配置项目，以使用 "Components"（组件）标签中的各个 JSF 组件套件。要使用组件套件，您需要下载所需的库并使用 Ant 库管理器创建具有组件套件库的新库。

image::images/jsf-components.png[title="指定 Faces servlet 选项和首选语言"]


. 单击 "OK"（确定）以完成更改并退出 "Project Properties"（项目属性）窗口。

在将 JSF 支持添加到项目中后，该项目的 `web.xml` 部署描述符修改为如下内容。（更改内容以*粗体*显示。）


[source,xml]
----

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    *<context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>*
    <welcome-file-list>
        <welcome-file>*faces/*index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>
----

*重要说明：*确认 `web.xml` 只包含一个 `<welcome-file>` 条目并且该条目包含 '`faces/`'，如示例中所示。这可以确保项目的欢迎页 (`index.xhtml`) 先传递 Faces servlet，然后再显示在浏览器中。它是正确呈现 Facelets 标记库组件所必需的。

在项目中注册 Faces Servlet，并且在请求 `index.xhtml` 欢迎页面时，该页将立即通过 Faces Servlet 进行传递。此外，请注意已添加了一个 `PROJECT_STAGE` 上下文参数的条目。将此参数设置为 "`Development`"，可在调试应用程序时为您提供有用的信息。有关详细信息，  有关详细信息，请参见 link:http://blogs.oracle.com/rlubke/entry/jsf_2_0_new_feature2[+http://blogs.oracle.com/rlubke/entry/jsf_2_0_new_feature2+]。

通过在 "Projects"（项目）窗口中展开项目的 "Libraries"（库）节点，可以查找 JSF 库。如果您使用的是 GlassFish Server 3.1.2 或 GlassFish Server 4 附带的默认库，则为 GlassFish Server 节点下显示的 `javax.faces.jar`。（如果您使用的是 GlassFish 的早期版本，则会显示 `jsf-api.jar` 和 `jsf-impl.jar` 库，而不会显示 `javax.faces.jar`。）

IDE 的 JSF 2.x 支持主要包括大量特定于 JSF 的向导以及由 Facelets 编辑器提供的特殊功能。您将在以下步骤中了解这些功能。有关详细信息，请参见 link:jsf20-support.html[+NetBeans IDE 中的 JSF 2.x 支持+]。


[[managedBean]]
== 创建受管 Bean

您可以使用 JSF 的受管 Bean 来处理用户数据并在请求之间保留这些数据。受管 Bean 是一个 link:http://en.wikipedia.org/wiki/Plain_Old_Java_Object[+POJO+]（Plain Old Java Object，简单的传统 Java 对象），可用于存储数据，并由容器（例如，GlassFish Server）使用 JSF 框架来管理。

POJO 本质上是一个 Java 类，包含了一个无参数的公共构造函数，并且该类遵循其属性的 link:http://download.oracle.com/javase/tutorial/javabeans/[+JavaBeans+] 命名惯例。

请看通过运行项目所生成的<<staticPage,静态页面>>，您需要一种机制：用于确定用户输入的数字是否与当前选择的一个数字相匹配，并且返回与此结果相应的视图。使用 IDE 的link:jsf20-support.html#managedBean[+受管 Bean 向导+]来创建受管 Bean 以实现此目的。您在下一部分创建的 Facelets 页将需要访问用户键入的数字和生成的响应。要启用此功能，请将 `userNumber` 和 `response` 属性添加到受管 Bean 中。

* <<usingManagedBean,使用受管 Bean 向导>>
* <<creatingConstructor,创建构造函数>>
* <<addingProperties,添加属性>>


[[usingManagedBean]]
=== 使用受管 Bean 向导

1. 在 "Projects"（项目）窗口中，右键单击 `jsfDemo` 项目节点，然后选择 "New"（新建）> "JSF Managed Bean"（JSF 受管 Bean）。（如果未列出 "Managed Bean"（受管 Bean），请选择 "Other"（其他）。然后从 "JavaServer Faces" 类别中选择 "JSF Managed Bean"（JSF 受管 Bean）选项。单击 "Next"（下一步）。）
2. 在向导中，输入以下内容：
* *Class Name（类名）：*UserNumberBean
* *Package（包）：*guessNumber
* *Name（名称）：*UserNumberBean
* *Scope（范围）：*Session（会话）

image::images/managed-bean.png[title="使用 JSF 受管 Bean 向导创建新的受管 Bean"]


. 单击 "Finish"（完成）。将会生成 `UserNumberBean` 类并在编辑器中将其打开。注意以下标注（*粗体*显示）：

[source,java]
----

package guessNumber;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

/**
 *
 * @author nbuser
 */
*@ManagedBean(name="UserNumberBean")
@SessionScoped*
public class UserNumberBean {

    /** Creates a new instance of UserNumberBean */
    public UserNumberBean() {
    }

}
----

因为您使用的是 JSF 2.x，所以可以使用标注来声明所有 JSF 特定的组件。在以前的版本中，您需要在 Faces 配置文件 (`faces-config.xml`) 中对其进行声明。

[tips]#要查看所有 JSF 2.1 标注的 Javadoc，请参见 link:http://javaserverfaces.java.net/nonav/docs/2.1/managed-bean-javadocs/index.html[+Faces 受管 Bean 标注规范+]。#


[[creatingConstructor]]
=== 创建构造函数

`UserNumberBean` 构造函数必须生成一个 0 到 10 之间的随机数字并将其存储到一个实例变量中。这就构成了应用程序的部分业务逻辑。

1. 为 `UserNumberBean` 类定义一个构造函数。输入以下代码（更改内容以*粗体*显示）。

[source,java]
----

public class UserNumberBean {

    *Integer randomInt;*

    /** Creates a new instance of UserNumberBean */
    public UserNumberBean() {
        *link:http://docs.oracle.com/javase/7/docs/api/java/util/Random.html[+Random+] randomGR = new Random();
        randomInt = new Integer(randomGR.link:http://docs.oracle.com/javase/7/docs/api/java/util/Random.html#nextInt%28int%29[+nextInt+](10));
        System.out.println("Duke's number: " + randomInt);*
    }

}
----

以上代码将生成一个 0 到 10 之间的随机数字，并将其输出到服务器日志中。



. 修复导入。为此，请单击显示在编辑器左旁注中的提示标记 (image:images/hint-icon.png[])，然后选择相应选项将 `java.util.Random` 导入到类。


. 再次运行项目（单击 "Run Project"（运行项目）(image:images/run-project-btn.png[]) 按钮，或者按 F6；在 Mac 上按 fn-F6）。当运行项目时，服务器的日志文件将自动在 "Output"（输出）窗口中打开。 

image::images/output1.png[title="服务器的日志文件自动在 &quot;Output&quot;（输出）窗口中打开"]

请注意，输出中并未列出 "`Duke's number:`"（从构造函数中也表明了这一点）。因为默认情况下 JSF 使用_延迟实例化_，所以未创建 `UserNumberBean` 对象。也就是说，只有当应用程序需要时才会创建和初始化特定范围中的 Bean。

link:http://javaserverfaces.java.net/nonav/docs/2.1/managed-bean-javadocs/index.html[+`@ManagedBean` 标注的 Javadoc+] 中描述了以下内容：

_如果 `eager()` 属性的值为 `true`，且 `managed-bean-scope` 的值为 "application"，则当应用程序启动时，运行时必须对该类执行实例化操作。必须在提供任何请求服务之前，对实例执行实例化和存储操作。如果 _eager_ 未指定或为 `false`，或者 `managed-bean-scope` 不是 "application"，则会发生受管 Bean 的默认“延迟”实例化和作用域存储。_


. 因为 `UserNumberBean` 属于会话范围，所以它将实现 `Serializable` 接口。

[source,java]
----

@ManagedBean(name="UserNumberBean")
@SessionScoped
public class UserNumberBean *implements Serializable* {
----
使用提示标记 (image:images/hint-icon.png[]) 将 `java.io.Serializable` 导入到类。


[[addingProperties]]
=== 添加属性

您在下一部分创建的 Facelets 页将需要访问用户键入的数字和生成的响应。为了便于实现此功能，请将 `userNumber` 和 `response` 属性添加到类中。

1. 首先声明一个名为 `userNumber` 的 `Integer`。

[source,java]
----

@ManagedBean(name="UserNumberBean")
@SessionScoped
public class UserNumberBean implements Serializable {

    Integer randomInt;
    *Integer userNumber;*
----


. 在编辑器中单击鼠标右键，然后选择 "Insert Code"（插入代码）（Alt-Insert 组合键；在 Mac 上为 Ctrl-I 组合键）。选择 "Getter and Setter"（Getter 和 Setter）。 

image::images/getter-setter.png[title="使用 IDE 生成属性的存取方法"]


. 选择 `userNumber` : `Integer` 选项。单击 "Generate"（生成）。

image::images/generate-getters-setters.png[title="使用 IDE 生成属性的存取方法"]

请注意，`getUserNumber()` 和 `setUserNumber(Integer userNumber)` 方法已添加到类中。



. 创建一个 `response` 属性。声明一个名为 `response` 的 `String`。

[source,java]
----

@ManagedBean(name="UserNumberBean")
@SessionScoped
public class UserNumberBean implements Serializable {

    Integer randomInt;
    Integer userNumber;
    *String response;*
----


. 为 `response` 创建一个 getter 方法。（本应用程序不需要 setter。）可以使用以上步骤 2 中显示的 IDE 的 "Generate Code"（生成代码）弹出式窗口来生成模板代码。但是就本教程而言，只需将以下方法粘贴到类中即可。

[source,html]
----

public String getResponse() {
    if ((userNumber != null) &amp;&amp; (userNumber.link:http://download.oracle.com/javase/6/docs/api/java/lang/Integer.html#compareTo(java.lang.Integer)[+compareTo+](randomInt) == 0)) {

        //invalidate user session
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        session.invalidate();

        return "Yay! You got it!";
    } else {

        return "<p>Sorry, " + userNumber + " isn't it.</p>"
                + "<p>Guess again...</p>";
    }
}
----
上面的方法执行了两个功能：
1. 测试用户输入的数字 (`userNumber`) 是否等于为会话生成的随机数字 (`randomInt`)，并相应地返回一个 `String` 响应。
2. 如果用户猜对了数字（即，如果 `userNumber` 等于 `randomInt`），则将使用户会话失效。这是必需的，以便当用户希望再玩一次时会生成一个新的数字。


. 在编辑器中单击鼠标右键，然后选择 "Fix Imports"（修复导入）（Alt-Shift-I 组合键；在 Mac 上为 ⌘-Shift-I 组合键）。将为以下内容自动创建导入声明：
* `javax.servlet.http.HttpSession`
* `javax.faces.context.FacesContext`

可以在编辑器中的项目上按 Ctrl-空格键以调用代码完成建议和文档支持。在 `FacesContext` 上按 Ctrl-空格键来查看 Javadoc 中的类描述。


image::images/documentation-support.png[title="按 Ctrl-空格键调用代码完成和文档支持"] 

单击 "documentation"（文档）窗口中的 "web browser"（Web 浏览器）(image:images/web-browser-icon.png[]) 图标以在外部 Web 浏览器中打开 Javadoc。



[[wire]]
== 将受管 Bean 连接到页面

JSF 的一个主要目的就是不需要编写样板代码来管理 <<pojo,POJO>> 及其与应用程序视图的交互。在上一部分中，您已看到这样的示例，当运行应用程序时，JSF 实例化了一个 `UserNumberBean` 对象。此概念称为link:http://martinfowler.com/articles/injection.html[+控制反转+] (IoC)，可以让容器负责管理应用程序的某些部分，否则这些部分就需要开发者编写重复的代码。

在上一部分，您已创建了一个生成 0 到 10 之间的随机数字的受管 Bean。还创建了两个属性，`userNumber` 和 `response`，分别代表用户输入的数字，和对用户所做猜测的响应。

在此部分，您将了解如何在 Web 页中使用 `UserNumberBean` 及其属性。JSF 通过其表达式语言 (EL) 来实现此功能。使用表达式语言将属性值绑定到应用程序 Web 页中包含的 JSF UI 组件。此部分还将演示如何利用 JSF 2.x 的隐式导航功能在索引页和响应页之间进行导航。

IDE 通过其代码完成和文档功能为此操作提供支持，您可以在编辑器中的项目上按 Ctrl-空格键调用这些功能。

首先对 `index.xhtml` 进行更改，然后对 `response.xhtml` 进行更改。在这两个页面中，将 HTML 窗体元素替换成其 JSF 的对应元素，如 link:http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/index.html[+JSF HTML 标记库+]中定义的元素。然后，使用 JSF 表达式语言将属性值与选定的 UI 组件绑定在一起。

* <<index,index.xhtml>>
* <<response,response.xhtml>>


[[index]]
=== index.xhtml

1. 在编辑器中打开 `index.xhtml` 页。双击 "Projects"（项目）窗口中的 `index.xhtml` 节点，或者按 Alt-Shift-O 组合键以使用 "Go to File"（转至文件）对话框。

索引页和响应页都已包含了本练习所需的 JSF UI 组件。只需取消其注释，并注释掉当前正在使用的 HTML 元素。


. 注释掉 HTML 窗体元素。为此，请按照下图所示突出显示 HTML 窗体元素，然后按 Ctrl-/ 组合键（在 Mac 上为 ⌘-/ 组合键）。

*注：*要突出显示该表单元素，可以单击该元素，然后用鼠标在编辑器中拖动；也可以使用键盘，按住 Shift 键并按方向键。 

image::images/comment-out.png[title="突出显示代码，然后按 Ctrl-/ 以注释掉代码"]

在编辑器中使用 Ctrl-/ 组合键（在 Mac 上为 ⌘-/ 组合键）启用/禁用注释。还可以将此快捷键应用于其他文件类型，如 Java 和 CSS。



. 取消 JSF HTML 窗体组件的注释。如下图所示突出显示组件，然后按 Ctrl-/ 组合键（在 Mac 上为 ⌘-/ 组合键）。

*注：*您可能需要按两次 Ctrl-/ 来取消注释代码。


image::images/comment.png[title="突出显示注释掉的代码，然后按 Ctrl-/ 以取消注释它"]

取消 JSF HTML 窗体组件的注释之后，编辑器指示 `<h:form>`、`<h:inputText>` 和 `<h:commandButton>` 标记未进行声明。

image::images/undeclared-component.png[title="编辑器针对未声明的组件提供错误消息"]


. 要声明这些组件，请使用 IDE 的代码完成将标记库名称空间添加到页面的 `<html>` 标记中。将光标放置在任何未声明的标记上方，然后按 Alt-Enter 组合键并单击 Enter 以添加建议的标记库。（如果有多个选项，请确保按 Enter 键之前选择了在编辑器中显示的标记。）JSF HTML 标记库名称空间将添加到 `<html>` 标记中（如以下*粗体*所示），并且错误指示符将消失。

*注：*如果 IDE 未提供用于添加标记库的选项，则您需要手动修改  ``<html>``  元素。


[source,java]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      *xmlns:h="http://xmlns.jcp.org/jsf/html"*>
----


. 使用 JSF 表达式语言将 `UserNumberBean` 的 `userNumber` 属性绑定到 `inputText` 组件。`value` 属性可用于指定所呈现组件的当前值。键入以下*粗体*显示的代码。

[source,java]
----

<h:form>
    <h:inputText id="userNumber" size="2" maxlength="2" *value="#{UserNumberBean.userNumber}"* />
----

JSF 表达式语言使用 `#{}` 语法。在这些分隔符内，会指定用点 (`.`) 分隔的受管 Bean 的名称和需要应用的 Bean 属性。现在，将窗体数据发送至服务器时，会使用属性的 setter (`setUserNumber()`) 将该值自动保存到 `userNumber` 属性中。此外，当请求页面且已设置 `userNumber` 的值时，该值将自动显示在呈现的 `inputText` 组件中。有关更多信息，请参见 link:http://docs.oracle.com/javaee/7/tutorial/doc/jsf-develop001.htm#BNAQP[+Java EE 7 教程的第 12.1.2 节：使用 EL 引用受管 Bean+]。



. 为单击窗体按钮时调用的请求指定目标。在 HTML 窗体版本中，可以使用 `<form>` 标记的 `action` 属性来完成此操作。在 JSF 中，可以使用 `commandButton` 的 `action` 属性。此外，由于 JSF 2.x 的隐式导航功能，您只需指定目标文件的名称，而无需文件扩展名。

键入以下*粗体*显示的代码。


[source,xml]
----

<h:form>
    <h:inputText id="userNumber" size="2" maxlength="2" value="#{UserNumberBean.userNumber}" />
    <h:commandButton id="submit" value="submit" *action="response"* />
</h:form>
----

JSF 运行时将搜索一个名为 `response` 的文件。JSF 运行时将假定文件扩展名与请求源自的文件 (`index*.xhtml*`) 所使用的扩展名相同，并在与源文件相同的目录（即 Web 根目录）中查找 `response.xhtml` 文件。

*注：*JSF 2.x 旨在帮助开发者更轻松地执行任务。如果您在本项目中使用的是 JSF 1.2，则需要在 Faces 配置文件中声明一条类似于以下内容的导航规则：


[source,xml]
----

<navigation-rule>
    <from-view-id>/index.xhtml</from-view-id>

    <navigation-case>
        <from-outcome>response</from-outcome>
        <to-view-id>/response.xhtml</to-view-id>
    </navigation-case>
</navigation-rule>
----

以下步骤 7 至 12 为可选操作。如果希望快速构建项目，请跳至 <<response,`response.xhtml`>>。



. 测试当处理请求时上述 EL 表达式是否实际调用了 `setUserNumber()` 方法。要执行此操作，请使用 IDE 的 Java 调试器。

切换到 `UserNumberBean` 类（按 Ctrl-Tab 组合键，然后从列表中选择文件。）在 `setUserNumber()` 方法签名上设置一个断点。可以通过单击左旁注来完成此操作。将显示一个红色的标记，表示已设置方法断点。

image::images/set-breakpoint.png[title="在编辑器的左旁注中单击以设置断点"]


. 单击 IDE 主工具栏中的 "Debug Project"（调试项目）(image:images/breakpoint-btn.png[]) 按钮。将启动调试会话，并在浏览器中打开项目欢迎页面。

*注：*

* 系统可能会提示您确认用于调试应用程序的服务器端口。
* 如果显示了 "Debug Project"（调试项目）对话框，请选择默认 "Server side Java"（服务器端 Java）选项，然后单击 "Debug"（调试）。


. 在浏览器中，将数字输入窗体中，然后单击 "submit" 按钮。


. 切换回 IDE 并检查 `UserNumberBean` 类。在 `setUserNumber()` 方法内挂起执行调试器。 

image::images/debugger-suspended.png[title="调试器根据断点挂起"]


. 打开调试器的 "Variables"（变量）窗口（选择 "Window"（窗口）> "Debugging"（调试）> "Variables"（变量），或者按 Ctrl-Shift-1 组合键）。将看到调试器挂起所在点的变量值。 

image::images/variables-window.png[title="使用调试器的 &quot;Variables&quot;（变量）窗口监视变量值"]

在上图中，`setUserNumber()` 签名中 `userNumber` 变量的值为 "`4`"。（窗体中输入了数字 4。）"`this`" 指的是为用户会话创建的 `UserNumberBean` 对象。在其下方，可看到 `userNumber` 属性的值目前为`空值`。



. 在调试器工具栏中，单击 "Step Into"（步入）(image:images/step-into-btn.png[]) 按钮。调试器将执行当前挂起的行。"Variables"（变量）窗口将刷新，表示执行产生的更改。 

image::images/variables-window2.png[title="&quot;Variables&quot;（变量）窗口在单步调试代码时刷新"] 

`userNumber` 属性现在被设置为窗体中输入的值。



. 从主菜单中选择 "Debug"（调试）> "Finish Debugger Session"（结束调试器会话）（Shift-F5 组合键；在 Mac 上为 Shift-Fn-F5 组合键）停止调试器。


[[response]]
=== response.xhtml

1. 在编辑器中打开 `response.xhtml` 页。双击 "Projects"（项目）窗口中的 `response.xhtml` 节点，或者按 Alt-Shift-O 组合键以使用 "Go to File"（转至文件）对话框。
2. 注释掉 HTML 窗体元素。突出显示 HTML `<form>` 起始标记和结束标记及其之间的代码，然后按 Ctrl-/ 组合键（在 Mac 上为 ⌘-/ 组合键）。

*注：*要突出显示该表单元素，可以单击该元素，然后用鼠标在编辑器中拖动；也可以使用键盘，按住 Shift 键并按方向键。



. 取消 JSF HTML 窗体组件的注释。突出显示 `<h:form>` 起始标记和结束标记及其之间的代码，然后按 Ctrl-/ 组合键（在 Mac 上为 ⌘-/ 组合键）。

此时，`<body>` 标记之间的代码如下所示：


[source,html]
----

<body>
    <div id="mainContainer">

        <div id="left" class="subContainer greyBox">

            <h4>[ response here ]</h4>

            <!--<form action="index.xhtml">

                <input type="submit" id="backButton" value="Back"/>

            </form>-->

            <h:form>

                <h:commandButton id="backButton" value="Back" />

            </h:form>

        </div>

        <div id="right" class="subContainer">

            <img src="duke.png" alt="Duke waving" />
             <!--<h:graphicImage url="/duke.png" alt="Duke waving" />-->

        </div>
    </div>
</body>
----

在取消 JSF HTML 窗体组件的注释后，编辑器将指示 `<h:form>` 和 `<h:commandButton>` 标签未进行声明。



. 要声明这些组件，请使用 IDE 的代码完成将标记库名称空间添加到页面的 `<html>` 标记中。

使用编辑器的代码完成支持将所需的 JSF 名称空间添加到文件中。当通过代码完成选择一个 JSF 或 Facelets 标记时，会自动将所需的名称空间添加到文档的根元素中。有关详细信息，请参见 link:jsf20-support.html#facelets[+NetBeans IDE 中的 JSF 2.x 支持+]。

将光标置于任何未声明的标记上，然后按 Ctrl-空格组合键。将显示代码完成建议和文档支持。

image::images/code-completion2.png[title="按 Ctrl-空格键调用代码完成建议和 &quot;documentation&quot;（文档）弹出式窗口"]

按 Enter 键。（如果有多个选项，请确保按 Enter 键之前选择了在编辑器中显示的标记。）JSF HTML 标记库名称空间将添加到 `<html>` 标记中（如以下*粗体*所示），并且错误指示符将消失。


[source,java]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      *xmlns:h="http://xmlns.jcp.org/jsf/html"*>
----


. 为用户单击窗体按钮时调用的请求指定目标。您希望设置一个按钮以便当用户单击该按钮时，会返回到索引页。要完成此功能，请使用 `commandButton` 的 `action` 属性。键入以*粗体*显示的代码。

[source,xml]
----

<h:form>

    <h:commandButton id="backButton" value="Back" *action="index"* />

</h:form>
----

*注：*通过键入 `action="index"`，将可依赖 JSF 的隐式导航功能。当用户单击窗体按钮时，JSF 运行时会搜索一个名为 `index` 的文件。JSF 运行时将假定文件扩展名与请求源自的文件 (`response*.xhtml*`) 所使用的扩展名相同，并在与源文件相同的目录（即 Web 根目录）中查找 `index.xhtml` 文件。



. 将静态的 "[ response here ]" 文本替换为 `UserNumberBean` 的 `response` 属性值。要执行此操作，请使用 JSF 表达式语言。输入以下内容（*粗体*）。

[source,html]
----

<div id="left" class="subContainer greyBox">

    <h4>*<h:outputText value="#{UserNumberBean.response}"/>*</h4>
----


. 运行项目（单击 "Run Project"（运行项目）(image:images/run-project-btn.png[]) 按钮，或者按 F6；在 Mac 上按 fn-F6）。当浏览器中显示欢迎页面时，输入一个数字，然后单击 `submit`。将看到响应页显示类似于以下的内容（如果您没有猜对数字）。 

image::images/response2.png[title="在浏览器中查看项目的当前状态"]

响应页的当前状态中有两处错误：

1. html `<p>` 标记显示在了响应消息中。
2. "Back" 按钮没有显示在正确的位置。（请与<<originalVersion,原始版本>>作比较。）

以下两个步骤分别更正了这两个问题。



. 将 `<h:outputText>` 标记的 `escape` 属性设置为 `false`。将光标置于 `outputText` 和 `value` 之间，插入一个空格，然后按 Ctrl-空格键调用代码完成。向下滚动以选择 `escape` 属性并检查文档。 

image::images/escape-false.png[title="按 Ctrl-空格键查看可能的属性值和文档"]

如文档所示，默认情况下，`escape` 值被设为 `true`。这意味着任何被正常解析为 html 的字符都包含在此字符串中，如上所示。将该值设置为 `false`，将使任何可解析为 html 的字符都像这样来呈现。

按 Enter 键，然后键入 `false` 作为值。


[source,xml]
----

<h4><h:outputText *escape="false"* value="#{UserNumberBean.response}"/></h4>
----


. 将 `<h:form>` 标记的 `prependId` 属性设置为 `false`。将光标置于 `<h:form>` 中 "`m`" 的后面并插入一个空格，然后按 Ctrl-空格键调用代码完成。向下滚动以选择 `prependId` 属性并检查文档。然后按 Enter 键，并键入 `false` 作为值。

[source,java]
----

<h:form *prependId="false"*>
----

JSF 应用内部 id 来跟踪 UI 组件。在当前的示例中，如果检查所呈现页面的源代码，将看到如下内容：


[source,xml]
----

<form id="j_idt5" name="j_idt5" method="post" action="/jsfDemo/faces/response.xhtml" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_idt5" value="j_idt5" />
    <input *id="j_idt5:backButton"* type="submit" name="j_idt5:backButton" value="Back" />
    <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="7464469350430442643:-8628336969383888926" autocomplete="off" />
</form>
----

窗体元素的 id 为 `j_idt5`，并且此 id 被置于包含在窗体中的 "Back" 按钮的 _ID_ 前面（如以上*粗体*显示）。因为 "Back" 按钮依赖于 `#backButton` 样式规则（在 `stylesheet.css` 中定义），所以在前置 JSF id 时，将禁用该规则。可以通过将 `prependId` 设置为 `false` 来避免这种情况。



. 再次运行项目（单击 "Run Project"（运行项目）(image:images/run-project-btn.png[]) 按钮，或者按 F6；在 Mac 上按 fn-F6）。在欢迎页面输入一个数字，然后单击 "Submit"。现在响应页显示了不带 `<p>` 标记的响应消息，并且 "Back" 按钮放在了正确的位置。 

image::images/response3.png[title="在浏览器中查看项目的当前状态"]


. 单击 "Back" 按钮。因为 `UserNumberBean` 的 `userNumber` 属性的当前值绑定到了 JSF `inputText` 组件，所以您之前输入的数字现在显示在文本字段中。


. 在 IDE 的 "Output"（输出）窗口中检查服务器日志（Ctrl-4 组合键；在 Mac 上为 ⌘-4 组合键），以确定正确的猜测数字是什么。

如果由于某种原因无法看到服务器日志，可以通过切换到 "Services"（服务）窗口（Ctrl-5 组合键；在 Mac 上为 ⌘-5 组合键）并展开 "Servers"（服务器）节点来打开日志。然后右键单击项目部署所在的 GlassFish Server，并选择 "View Server Log"（查看服务器日志）。如果在服务器日志中看不到该数字，请尝试通过右键单击项目节点并选择 "Clean and Build"（清理并构建）来重新构建应用程序。



. 键入正确的数字，然后单击 "Submit"。应用程序将您的输入与当前保存的数字进行比较，并显示相应的消息。 

image::images/yay.png[title="在输入匹配号时显示正确响应"]


. 再次单击 "Back" 按钮。请注意，以前输入的数字在文本字段中不再显示。回想一下，当猜对了数字后，`UserNumberBean` 的 `getResponse()` 方法就会<<getResponse,使当前的用户会话失效>>。



[[template]]
== 应用 Facelets 模板

Facelets 已成为面向 JSF 2.x 的标准显示技术。Facelets 是一个小型的模板框架，支持所有 JSF UI 组件并用于为应用程序视图构建和呈现 JSF 组件树。在出现 EL 错误时，Facelets 还通过检查堆栈跟踪、组件树和作用域变量来提供开发支持。

尽管您也许还没有意识到，到目前为止您在本教程中所使用的 `index.xhtml` 和 `response.xhtml` 文件就是 Facelets 页。Facelets 页使用 `.xhtml` 扩展名，因为您是在 JSF 2.x 项目中工作（JSF 2.x 库包含了 Facelets JAR 文件。），所以视图可以正确地呈现 JSF 组件树。

此部分旨在帮助您熟悉 Facelets 模板。对于包含许多视图的项目，应用一个为多个视图定义了结构和外观的模板文件通常是很有用的。在为请求提供服务时，应用程序将动态准备的内容插入到模板文件中，并将结果发送回客户端。尽管本项目仅包含两个视图（欢迎页面和响应页面），但很容易看到这两个视图包含了大量的重复内容。可以将此重复内容提取到 Facelets 模板中，然后创建模板客户端文件来处理特定于欢迎页面和响应页面的内容。

IDE 提供了一个用于创建 Facelets 模板的 link:jsf20-support.html#faceletsTemplate[+Facelets 模板向导+]，和一个用于创建依赖于模板的文件的 Facelets 模板客户端向导。此部分将使用这些向导。

*注：*IDE 还提供了可为项目创建各个 Facelets 页的 JSF 页向导。有关详细信息，请参见 link:jsf20-support.html#jsfPage[+NetBeans IDE 中的 JSF 2.x 支持+]。

* <<templateFile,创建 Facelets 模板文件>>
* <<templateClient,创建模板客户端文件>>


[[templateFile]]
=== 创建 Facelets 模板文件

1. 创建一个 Facelets 模板文件。按 Ctrl-N 组合键（在 Mac 上为 ⌘-N 组合键）来打开文件向导。选择 "JavaServer Faces" 类别，然后选择 "Facelets Template"（Facelets 模板）。单击 "Next"（下一步）。
2. 键入 `template` 作为文件名。
3. 从八种布局样式中选择任意一种，然后单击 "Finish"（完成）。（您将使用现有的样式表，所以无论您选择的是哪种布局样式都没有关系。）

image::images/layout-style.png[title="通过 Facelets 模板向导可从公用布局样式中进行选择"] 

该向导会基于您的选择生成 `template.xhtml` 文件和附带的样式表，并将这些文件置于项目 Web 根目录内的 `resources` > `css` 文件夹中。

完成向导之后，模板文件在编辑器中打开。要在浏览器中查看模板，请右键单击编辑器并选择 "View"（视图）。



. 检查模板文件标记。请注意以下要点：
* 在页面的 `<html>` 标记中声明了 `facelets` 标记库。标记库具有 `ui` 前缀。

[source,java]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      *xmlns:ui="http://xmlns.jcp.org/jsf/facelets"*
      xmlns:h="http://xmlns.jcp.org/jsf/html">
----
* Facelets 页使用 `<h:head>` 和 `<h:body>` 标记而不是 html 的 `<head>` 和 `<body>` 标记。通过使用这些标记，Facelets 可以构造一个包含整个页面的组件树。
* 该页引用了在完成向导操作时同时创建的样式表。

[source,xml]
----

<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    *<link href="./resources/css/default.css" rel="stylesheet" type="text/css" />*
    *<link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />*
    <title>Facelets Template</title>
</h:head>
----
* 对于每个与所选择布局样式相关联的分栏，页面主体中都使用 `<ui:insert>` 标记。每个 `<ui:insert>` 标记都有一个 `name` 属性用于标识分栏。例如：

[source,html]
----

<div id="top">
    *<ui:insert name="top">Top</ui:insert>*
</div>
----


. 重新检查<<staticPage,欢迎>>和<<responsePage,响应>>页面。这两个页面之间唯一更改的内容是包含在灰色方块中的标题和文本。因此，模板可以提供其余所有内容。


. 将您的模板文件的全部内容替换为如下内容。

[source,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://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link href="css/stylesheet.css" rel="stylesheet" type="text/css" />

        <title><ui:insert name="title">Facelets Template</ui:insert></title>
    </h:head>

    <h:body>

        <div id="left">
            <ui:insert name="box">Box Content Here</ui:insert>
        </div>

    </h:body>

</html>
----
上述代码实现了以下更改：
* 项目的 `stylesheet.css` 文件替换了由向导创建的模板样式表引用。
* 除了一个名为 `box` 的标记之外，所有 `<ui:insert>` 标记（及其包含的 `<div>` 标记）都已删除。
* 在页面标题周围放置了 `<ui:insert>` 标记对，并命名为 `title`。


. 将 `index.xhtml` 或 `response.xhtml` 文件中的相关代码复制到模板中。将以下*粗体*显示的内容添加到模板文件的 `<h:body>` 标记中。

[source,html]
----

<h:body>
    *<div id="mainContainer">*
        <div id="left" *class="subContainer greyBox"*>
            <ui:insert name="box">Box Content Here</ui:insert>
        </div>
        *<div id="right" class="subContainer">
            <img src="duke.png" alt="Duke waving" />
        </div>
    </div>*
</h:body>
----


. 运行项目。当欢迎页面在浏览器中打开时，将 URL 修改为以下内容：

[source,java]
----

http://localhost:8080/jsfDemo/faces/template.xhtml
----
模板文件将如下所示： 

image::images/facelets-template.png[title="在浏览器中查看 Facelets 模板"]

此项目现在包含了一个可为所有视图提供外观和结构的模板文件。您现在可以创建调用了模板的客户端文件。


[[templateClient]]
=== 创建模板客户端文件

为欢迎页和响应页创建模板客户端文件。将欢迎页面的模板客户端文件命名为 `greeting.xhtml`。对于响应页，其模板客户端文件则为 `response.xhtml`。


==== greeting.xhtml

1. 按 Ctrl-N 组合键（在 Mac 上为 ⌘-N 组合键）以打开新建文件向导。选择 "JavaServer Faces" 类别，然后选择 "Facelets Template Client"（Facelets 模板客户端）。单击 "Next"（下一步）。
2. 键入 `greeting` 作为文件名。
3. 单击 "Template"（模板）字段旁边的 "Browse"（浏览）按钮，然后使用显示的对话框导航到上一部分创建的 `template.xhtml` 文件。 

image::images/template-client.png[title="Facelets 模板客户端向导"]


. 单击 "Finish"（完成）。将生成新的 `greeting.xhtml` 模板客户端文件并显示在编辑器中。


. 检查标记。请注意以*粗体*突出显示的内容。

[source,xml]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

    <body>

        <ui:composition *template="./template.xhtml"*>

            <ui:define *name="title"*>
                title
            </ui:define>

            <ui:define *name="box"*>
                box
            </ui:define>

        </ui:composition>

    </body>
</html>
----
模板客户端文件通过使用 `<ui:composition>` 标记的 `template` 属性来引用模板。因为模板包含了 `title` 和 `box` 的 `<ui:insert>` 标记，所以该模板客户端包含了这两个名称的 `<ui:define>` 标记。您在 `<ui:define>` 标记之间指定的内容就是将插入到模板中相应名称的 `<ui:insert>` 标记之间的内容。


. 指定 `greeting` 作为文件的标题。进行以下更改（*粗体*显示）。

[source,xml]
----

<ui:define name="title">
    *Greeting*
</ui:define>
----


. 切换到 `index.xhtml` 文件（按 Ctrl-Tab 组合键），然后复制通常会出现在灰色方块（显示在所呈现的页面中）中的内容。然后切换回 `greeting.xhtml`，并将其粘贴到模板客户端文件中。（更改内容以*粗体*显示。）

[source,xml]
----

<ui:define name="box">
    *<h4>Hi, my name is Duke!</h4>

    <h5>I'm thinking of a number

        <br/>
        between
        <span class="highlight">0</span> and
        <span class="highlight">10</span>.</h5>

    <h5>Can you guess it?</h5>

    <h:form>
        <h:inputText size="2" maxlength="2" value="#{UserNumberBean.userNumber}" />
        <h:commandButton id="submit" value="submit" action="response" />
    </h:form>*
</ui:define>
----


. 为该文件声明 JSF HTML 标记库。将光标置于任意一个被标记为错误的标记（任何使用 "`h`" 前缀的标记）上，然后按 Ctrl-空格组合键。然后从代码完成建议列表中选择标记。标记库名称空间将添加到该文件的 `<html>` 标记中（如以下*粗体*所示），并且错误指示符将消失。

[source,java]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      *xmlns:h="http://xmlns.jcp.org/jsf/html"*>
----

如果将光标置于 `<h:form>` 中的 "`m`" 后面，然后按 Ctrl-空格键，则名称空间将自动添加到文件中。如果在按 Ctrl-空格键时仅有一个逻辑选项可用，则会立即将其应用于文件中。当在标记上调用代码完成时，会自动声明 JSF 标记库。


==== response.xhtml

因为此项目已经包含了一个名为 `response.xhtml` 的文件，并且您现在已经知道了模板客户端文件应该是什么样的，那么就请将现有的 `response.xhtml` 修改为模板客户端文件。（就本教程而言，只需复制和粘贴已提供的代码即可。）

1. 在编辑器中打开 `response.xhtml`。（如果该文件已打开，请按 Ctrl-Tab 组合键并选择该文件。）将整个文件的内容替换为以下代码。

[source,xml]
----

<?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://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

    <body>

        <ui:composition template="./template.xhtml">

            <ui:define name="title">
                Response
            </ui:define>

            <ui:define name="box">
                <h4><h:outputText escape="false" value="#{UserNumberBean.response}"/></h4>

                <h:form prependId="false">

                    <h:commandButton id="backButton" value="Back" action="greeting" />

                </h:form>
            </ui:define>

        </ui:composition>

    </body>
</html>
----
请注意，该文件除了在 `title` 和 `box` 的 `<ui:define>` 标记之间指定的内容外，与 `greeting.xhtml` 是相同的。


. 在此项目的 `web.xml` 部署描述符中，修改欢迎文件条目以使 `greeting.xhtml` 成为在运行此应用程序时所打开的页面。

在 "Projects"（项目）窗口中，双击 "Configuration Files"（配置文件）> `web.xml` 以在编辑器中将其打开。在 "Pages"（页面）标签下面，将 "Welcome Files"（欢迎文件）字段更改为 "`faces/greeting.xhtml`"。 

image::images/welcome-files.png[title="更改部署描述符中的 &quot;Welcome Files&quot;（欢迎文件）条目"]


. 运行该项目以查看其在浏览器中的外观。按 F6（在 Mac 上按 fn-F6），或单击主工具栏中的 "Run Project"（运行项目）(image:images/run-project-btn.png[]) 按钮。该项目将被部署到 GlassFish Server 并在浏览器中打开。

使用了 Facelets 模板和模板客户端文件，该应用程序的行为方式仍与以前一样。通过将应用程序欢迎页和响应页中的重复代码提取出来，可以成功减小应用程序的大小，并避免在以后添加更多页面时可能编写更多的重复代码。在使用大型项目时，这样便可以更有效更轻松地维护开发。

link:/about/contact_form.html?to=3&subject=Feedback:%20Introduction%20to%20JSF%202.0[+发送有关此教程的反馈意见+]



[[seealso]]
== 另请参见

有关 JSF 2.x 的详细信息，请参见以下资源。


=== NetBeans 文章和教程

* link:jsf20-support.html[+NetBeans IDE 中的 JSF 2.x 支持+]
* link:jsf20-crud.html[+通过数据库生成 JavaServer Faces 2.x CRUD 应用程序+]
* link:../../samples/scrum-toys.html[+Scrum 玩具 - JSF 2.0 完整样例应用程序+]
* link:../javaee/javaee-gettingstarted.html[+Java EE 应用程序入门指南+]
* link:../../trails/java-ee.html[+Java EE 和 Java Web 学习资源+]


=== 外部资源

* link:http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[+JavaServer Faces 技术+]（官方主页）
* link:http://jcp.org/aboutJava/communityprocess/final/jsr314/index.html[+JSR 314：JavaServer Faces 2.0 的规范+]
* link:http://docs.oracle.com/javaee/7/tutorial/doc/jsf-develop.htm[+Java EE 7 教程的第 12 章：使用 JavaServer Faces 技术进行开发+]
* link:http://javaserverfaces.dev.java.net/[+GlassFish 项目 Mojarra+]（JSF 2.x 的正式引用实现）
* link:http://forums.oracle.com/forums/forum.jspa?forumID=982[+OTN 论坛：JavaServer Faces+]
* link:http://www.jsfcentral.com/[+JSF 中心+]


=== 博客

* link:http://www.java.net/blogs/edburns/[+Ed Burns+]
* link:http://www.java.net/blogs/driscoll/[+Jim Driscoll+]
