vlambda博客
学习文章列表

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

Chapter 2. JavaServer Faces Lifecycle

 

“世界上没有两个人是一样的,音乐必须是这样,否则就不是音乐”

 
  --Billie Holiday

长期以来,Java 在服务器端取得了圆满成功:自 2000 年以来。企业已将 JVM、Java 编程语言和丰富的框架作为企业软件的首选平台。那么,作为数字 Web 工程师,我们继续信任 JVM 是否正确?我认为这个问题的答案是肯定的,因为你正在阅读这本书!

本章是JavaServer FacesJSF) 概念。我们将从 JSF 的历史和目的以及它与基本设计模式的关系开始:Model-View-ControllerMVC)。我们将探讨 JSF 中的生命周期概念,这是将其与其他 Java Web 应用程序框架区分开来的关键概念之一。此外,我们将检查一些 JSF 代码,包括托管 bean 的邪恶概念。我们还将介绍 JSF 应用程序如何在 POJO 和页面之间导航。为了使这一点更加甜蜜,我们将为页面作者使用强大的表达语言。当我们读完这一章时,我们将打下坚实的知识基础。

Introduction to JSF


JSF 是从组件模型构建 Web 用户界面的规范。它包含一个 MVC 和模板框架。 JSF 是 Java EE 平台的标准库。 Java 社区进程 (< strong>JCP) 控制规范,以及当前的< /a>版本为 JSF 2.2,由 Java Specification Request (JSR) 334 (https://www. jcp.org/en/jsr/detail?id=344)。

最初,JSF 背后的承诺是为服务器端 Java 带来快速的用户界面开发。在 JSF 最初构想时,这种说法是正确的。但是当然,如​​果您宁愿不编写大量 JavaScript 代码和手工制作的样板来处理 HTTP 请求的转换,它仍然很有用到 Java 调用和返回页面响应。自 2004 年 JSF 1.0 诞生以来,Web 技术,尤其是数字开发已经从网页中跃升。当时,JavaScript 作为一种编程语言并没有像现在这样被重视;没有响应式网页设计,对移动网页编程的需求肯定更少。如今,默认情况下经常会看到诸如移动优先或数字等术语。这意味着网站会考虑各种屏幕尺寸和设备,并认识到人们可以在智能手机或平板电脑上查看内容。有些人(您的目标客户)无法使用台式电脑或笔记本电脑。

Tip

参见 Cameron Moll 开创性的——但现在有点过时了——关于移动网页设计的电子书(http://mobilewebbook.com /)。英国政府非常重视“默认数字”一词 ( https://www.gov.uk/service-manual/digital-by-default),紧随其后的是美国数字服务(https://playbook.cio.gov/)。

JSF 被认为是一种用户界面技术,它甚至可以让 Java 工程师以与 JavaFX(或 Swing)应用程序相同的方式构建前端。这个想法是允许开发人员(而不是设计人员)使用自定义编辑器组装 HTML 页面。 JSF 应用程序被设计成主题。该框架的目的是允许渲染工具包产生不同形式的输出。渲染工具包可能会生成 PDF 输出,另一种类型会生成 HTML 输出,还有一种会生成 无线应用协议 (WAP)(WAP 是一项备受关注的技术在苹果于 2007 年生产第一部 iPhone 之前)。科技在飞速发展!

尽管 JSF 作为产生严肃应用程序的 Web 技术受到很多批评,但它得到了 Java EE 平台的支持。 Facelets 是一个有用的模板框架,用于为网页构建可共享的组件和部分内容。 JSF 有一个集成到 POJO 的生命周期模型,这意味着它可以与上下文和依赖注入 (CDI) bean 无缝协作。此外,JSF 一直与数字环境中发生的变化保持同步。 JSF 2.2 支持 HTML5 友好的标记。它支持 AJAX 事件并允许将事件排队。它允许 HTML5 内容的所有元素具有 W3C 认可 ID 属性。 JSF 2.2 引入了 Faces Flow,它增加了引导用户浏览屏幕、工作流和向导集的能力。最重要的是,JSF 2.2 (JSR 334) 代表了对 Java EE 平台持续支持基于标准组件的框架的承诺。

Tip

Mojarra 2.2

为了让 JSF 成为 Java EE 平台的标准,它需要 JSR 和参考实现。对于 JSF,参考实现项目称为 Mojarra。 软件是开源的,由 Oracle Corporation (https://javaserverfaces.java.net/)。参考实现是 GlassFish 4 应用服务器的一部分。

JSF 1.0 and 2.0 history

JSF 的概念在 2001 年左右首次被讨论。它是 Sun Microsystems 项目的一部分,名为 Project Rave,然后宣布为 JSR 127。 技术,虽然是对Apache等时代基于action-request的框架的改进Struts 在 2003 年和 2004 年受到了不冷不热的宣传。2004 年发布了维护版本 1.1,但直到 2006 年,JSF 1.2 规范才成为总括规范 Java EE 5 的正式部分。

然而,此时,开发人员的思想共享已经演变为 AJAX 技术、部分应用程序和非 JVM 软件,例如 Ruby 和 Ruby on Rails。 JSF 1.2 受到平台默认模板技术 JavaServer Pages 的阻碍。 JSP 被证明不适合 JSF,因为请求拦截和响应生成的生命周期本质上是不兼容的。对替代方案的搜索导致了 Facelets 的创建,这些 Facelets 旨在明确地与 JSF 一起工作。

2009 年,Facelets 成为 JSF 2.0 (JSR 314) 中的默认模板解决方案,这也是 Java EE 6 的一部分。JSF 2.0 为验证和转换添加了注解。 JSF 2.0 定义了一个标准的 AJAX 组件生命周期,并且还增加了对图形编辑器的改进。 JSF 2.0 引入了 Web 内容的资源句柄,包括图像、JavaScript 和 CSS 文件。

Key JSF 2.2 features

JSF 2.2 规范的大票 特性如下:

  • 它提供对 HTML5 友好标记的支持,这对网页设计师和界面开发人员来说是一个福音。

  • Resource Library Contracts 是 JSF 中的一个新系统,用于通过捆绑 Facelet 视图、组件、样式表和其他资源(包括国际化)来构建可重用的主题。

  • 它提供了与 Java EE 7 总括规范一致的新 URI 定位器。甲骨文公司在 2010 年收购了 Sun Microsystems,因此,http 形式的旧 URI: //java.sun.com/jsf/core 被转换为 http: //xmlns.jcp.org/jsf/core 反映JCP web 域的命名空间。

  • Faces Flows 允许将应用程序建模为页面和视图的有向图。使用 Faces Flows,我们可以根据用户界面构建工作流应用程序的基础。作为数字工程师,我们可以将应用程序的细分组合成一个更大的整体。这些类型的工作流适用于 CDI bean 的会话范围。您将在 第 6 章JSF 流和技巧< /em>

  • 无状态视图允许开发人员构建没有服务器端状态的组件。通常,JSF 组件会将用户界面组件的状态保存在服务器或客户端上,但有时视图不需要这种额外的资源,因此,无状态视图提供了服务器上 Web 应用程序的可伸缩性。

  • 它提供了从客户端窗口正确处理浏览器内容的能力:选项卡、浏览器窗口、弹出对话框或模式对话框。

JSF 2.2 向后兼容 Faces 2.1 和 2.0。针对 Faces 2.0 或 2.1 构建的应用程序不需要更改即可与 Faces 2.2 一起运行;但是,反过来,使用特定的 2.2 功能将无法在这些旧环境中运行。

JSF 基于以下 Java API 规范:

  • JavaServer Pages 2.2 和 JavaServer Pages 标记库 (JSTL) 1.2

  • Java 小服务程序 3.0

  • Java SE 7

  • Java EE 7

  • Java Bean 1.01

Why choose JSF over alternatives?

JSF 是迄今为止唯一被认证为 JCP 标准的 Java Web 应用程序框架。当然,还有其他选择;事实上,可能有多达 100 种不同的 Java Web 框架,并且其中大部分将是开源的。但是,它们会根据代码库的愿景、实现、年龄以及实际维护它作为存储库的人而有所不同。如果您的应用程序所针对的 Web 框架是用昨天的技术构建的,那么这对企业来说是没有好处的,因为 Web 在不断发展。同样,Web 框架必须与时俱进,否则最终将变得无关紧要。企业相信 JSF 是一个标准,可以保证该技术将得到长期支持。

Tip

事实上,MVC (JSR 371) 将是 Java EE 8 中的另一个标准 Web 应用程序框架。您将在 第 9 章、Java EE MVC 框架

完全可以理解,应用程序架构师可能希望选择 JSF 以外的 Web 框架来满足他们的业务需求。 Apache Struts 2、Spring MVC 和 Apache Wicket 是我顺便提及的几个。 Apache Struts 2 和 Spring MVC 通常被认为是面向请求的框架。 Apache Wicket 是一个面向组件的框架,是 JSF 的直接竞争对手。 Apache Struts 是 2000 年代初期最著名的 Web 应用程序框架之一,当然也是第一个打破常规的框架。

Web 框架的世界并不止于 Java。大多数开发人员都听说过 Ruby on Rails,它是一种脱离 JVM 的技术。一些工程师会了解 Java 和 Scala 开发人员的 Play 框架,然后还有基于 Groovy 语言的解决方案,例如 Grails。

无论您为 Web 应用程序选择什么框架,本质上都为您的开发人员决定了基于 Java 的前端架构。无论你做什么,我强烈建议不要发明自己的 Web 应用程序框架。开源的优势在于拥有来自数千家不同公司、不同事业和文化的开发人员社区。

如果您选择 JSF,那么作为客户,您很可能希望维持您在 Java 平台上的投资。您的 JSF 企业应用程序的核心优势是丰富的组件,并且您依靠模型添加了默认的好处,例如更容易验证、类型转换和 HTTP 请求参数的映射以 bean 属性。许多经验丰富的 Java EE 工程师将具有 JSF 框架方面的经验,因此您将成为一个很棒的公司。

The MVC design pattern


MVC 设计描述了一组设计模式,旨在将用户界面的关注点与语义绑定它们的应用程序逻辑分开。模型描述了业务逻辑。视图表示表示——用户感知并与之交互的抽象表面。 Controller 表示处理模型和视图之间交互的组件。 MVC 的最初想法源于 Trygve Reenskaug,他在 1970 年代在 Smalltalk 编程语言中引入了这个概念。该模式随后在 Smalltalk-80 中实现和普及,然后被更广泛的软件工程社区采用。 MVC 以其关于分工和组件之间职责分离的思想而闻名。

我们称其为 MVC 模式,因为复数术语将经典模式的一组相关派生描述为组模式。

MVC 模式 随后演变,产生了诸如分层模型-视图-控制器 (HMVC), Model-view-presenter (MVP), 模型视图 ViewModelMVVM)和 其他使 MVC 适应不同上下文的人。

MVC in JSF

MVC 如何映射到 JSF?这已在 以下几点中得到解答:

  • 模型:在 JSF 和 Java EE 中,模型是一个组件或一组组件 处理业务数据和逻辑。该模型可以是 CDI bean、EJB 或其他与 Web 容器和 JSF 框架的生命周期兼容的组件。

  • Controller:经典设计模式中控制器逻辑的很多职责都由框架负责。在 JSF 中,可以将 控制器的开始视为 FacesServlet,它负责将传入的 HTTP 请求分派到正确的托管 bean。

  • View:在 JSF 中,view 是包含UI 组件及其各自的 bean。通常,视图是用页面描述语言来描述的,JSF 2.0 就是 Facelets。 JSF 的渲染工具包将 UI 组件和 bean 组成一个完整的页面。

下图从 JSF 框架的角度说明了 MVC 模式:

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

模型视图控制器模式是 JSF 框架的术语

Facelets


JSF 规范 定义了 View Declaration Language (VDL) 来呈现页面的输出。在 JSF 1.0 中,这是 JavaServer Pages;但在 JSF 2.0 中,默认情况下将 VDL 更改为 Facelets。 Facelets 是 JSF 2.0 的默认视图处理程序,并被定义为 XHTML 文件。

Facelets 可以 在模板情况下使用。 Facelets 文件可以将主模板作为合成引用,并且视图可以提供看起来像提供给模板的千篇一律的内容。使用参考模板的 Facelet 称为模板客户端。模板客户端中的占位符内容将覆盖主模板中的默认内容。通过这种方式,可以重复使用 Facelets 以共享内容。模板客户端可以成为主模板,因此可以导出视图层次结构。

Facelets 还提供自定义标签的重用。工程师可以通过 XHTML 文件和元数据编写自己的自定义标签。设计者和开发者将通过标签库描述符文件提供内容。

Facelets 提供的最后一个模板选项是复合组件组合。这种 机制允许在其他 Facelet 视图中重用组合,使它们看起来像一流的组件。然而,模板文件必须在一个特殊的目录中创建,以允许内部合成处理程序成功。

The request processing lifecycle


JSF 有一个围绕 HTTP 协议构建的请求-响应处理生命周期。 JSF 建立在 Java Servlet 规范之上,该规范负责翻译请求用户代理,在大多数情况下,它是一个 Web 浏览器一个已知的端点。对于 JSF,第一个调用端口是 javax.faces.webapp.FacesServlet。此 servlet 将简单地将传入请求分派给控制器,并且此组件可以选择生成响应或将输出委托给内部 JSF 控制器实现。

JSF 在请求处理生命周期中存在三种情况。第一个是使用 Faces 请求调用 JSF 控制器,最终生成 Faces 响应。

第二个是检索资源的请求,例如 CSS 或 JavaScript 文件或图像或其他媒体文件。但是,不需要执行逻辑的 Faces 资源请求会导致 JSF 框架将输出提供为 Faces 资源响应。

最后一个是检索与 JSF 无关的内容的页面请求,这称为 Non-Faces 请求,随后派生出 Non-Faces 响应。对 JAX-RS 服务端点的 HTTP 请求是 Non-Faces 请求和响应的示例。让我们看一下下图:

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

JSF 请求和响应处理

JSF 框架 首先确定传入请求是否针对资源。如果是,则框架提供资源并将字节、内容类型和数据发送到用户代理。

当传入的请求被视为 Face 请求时,就会发生有趣的工作; JSF 框架在线性工作流中处理此处理。这个过程称为执行和渲染生命周期。

The execute and render lifecycle

下图显示了处理 Faces 请求的 JSF 生命周期:

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

JSF 框架内的执行和渲染生命周期阶段

标准请求处理生命周期从对 Restore View 阶段的 Faces 请求刺激开始。 JSF 为生命周期维护一个 javax.faces.context.FacesContext 实例。此对象实例包含与单个 Faces 请求关联的所有信息。 FacesContext 被传递到各个阶段。

Restore View

恢复视图 是生命周期的一个阶段,其中 JSF 框架确保树组件及其状态与最初在响应中生成视图时的表单相匹配。换句话说,JSF 必须准确地重建视图,然后才能开始插入更改并应用来自 Faces 请求的表单属性值。这个阶段存在的原因是整体输入的状态可以在请求之间动态变化。以下是技术深度描述。

恢复视图确定请求是回发还是根据算法。 JSF 中的每个视图都有其唯一标识符,viewId,这通常存储在框架实现内部的地图集合中。框架在与视图关联的 javax.faces.application.ViewHandler 实例上调用 initView() 方法。恢复视图构造或检索视图以显示给用户代理。

如果视图已经存在,则请求是回发。然后 JSF 将使用以前保存的状态恢复带有 viewId 的视图。状态可以存储在服务器或客户端上。此行为是从应用程序的 Web XML 部署行为配置的。

对于一个全新的视图,JSF 创建一个类型为 javax.faces.component.UIViewRoot 的新实例,该实例最初为空并在其上设置相关属性,例如语言环境和字符集。然后 JSF 用树数据结构中的 UI 组件填充视图。

Apply Request Values

组件树恢复后,JSF 将请求信息参数映射到组件 属性。框架 迭代树中的组件对象。每个组件都从请求对象中检索数据,这些数据通常是请求参数,但也可以是 cookie、会话属性,甚至是标头参数。因此,新值与 UI 组件一起存储在本地。这些值是从请求信息中提取的,在这个阶段,这些值仍然是字符串。此阶段称为应用请求值阶段。

在这个阶段,JSF 将尝试在适当的地方对组件属性应用转换。如果转换或验证失败,则错误消息将在 FacesContext 上排队。

当单击命令按钮或链接时,应用请求值阶段将事件添加到内部 JSF 事件队列。 JSF 有一些特殊条件,允许事件处理程序中断处理的线性流程并跳到最后阶段:呈现响应。

Process Validations

流程验证阶段 是存储提交的字符串值的 阶段与组件一起,被转换为本地值。这些本地值可以是任何类型的 Java 对象。在这个阶段,与组件关联的验证器可以验证本地值的值。如果验证通过并且所有 所需的验证器都被成功调用,那么JSF生命周期继续到下一阶段。如果验证失败或在前一个生命周期阶段(应用请求值阶段)出现转换错误,则 JSF 框架直接跳到呈现响应阶段。然后,Web 用户就有机会在 HTML 输入表单中输入正确的数据。

作为一名 JSF 开发人员,您可以将验证器附加到带有输入的 UI 组件,这对检查很重要。

Update Model Values

转换和验证 阶段之后,JSF 进入更新模型值阶段。此时,局部值被认为是安全的,以便更新模型。请记住,在 JSF 与 MVC 的说法中,模型很可能是您的托管支持 bean、CDI bean 或 EJB 或聚合对象。 JSF 更新组件引用的 bean。

Invoke Application

在生命周期中,我们到达了模型已经更新,转换和验证已经已应用。 JSF 将此称为 Invoke Application 阶段,最后在这里调用业务逻辑。 JSF 调用由命令按钮或链接组件的操作方法命名的方法。 Invoke Application 阶段是用户提交 HTML 表单或调用导航锚链接的结果,因此 JSF 框架执行支持 bean 的相应方法。

该方法可以选择返回一个简单的结果字符串。从 JSF 2.0 开始,方法可以返回一个简单的字符串,该字符串通过其名称引用视图。或者,该方法可以使用 FacesContext 实例以编程方式构建自己的响应,或者可以返回传递给导航处理程序的导航视图 ID,导航处理程序依次查找下一页。

Render Response

生命周期的最后一个阶段渲染响应阶段。这个阶段需要对 Faces 响应进行编码,并且 JSF 框架将此输出发送到发出请求的用户代理,该用户代理通常是 Web 浏览器。一旦数据通过网络发送到客户端,请求和响应的生命周期就完成了。新的生命周期从下一个请求开始。

Event handling

某些阶段之间,您会注意到Process Event 阶段。 JSF 允许针对框架注册侦听器以观察事件。这些被称为阶段监听器。它们之所以特别,是因为它们可以在行为上处于活跃状态并导致生命周期跳过或者他们可以被动地只监视应用程序感兴趣的用户界面的某些方面。这些微型扩展点对于应用程序构建者来说非常有用和强大,因此是 JSF 和其他 Web 框架之间的主要区别。

A basic JSF example


我们已经介绍了足够多的有关 JSF 框架的理论。我认为是时候让我的读者看到一些代码了。第一个代码是用于在站点上显示基本网页的 XHTML 文件。源代码可在作者的公共 GitHub 帐户中的本书网站上获得,网址为 http://github。 com/peter_pilgrim/digital_javaee7

这是初始 Facelets 视图的 XHTML 源代码,文件名为 index.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" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core">

  <h:head>
    <title>Digital Java EE 7 - Sample JSF</title>
  </h:head>
  <h:body>
    This is the simplest JSF example possible.

    <h:form>
      <h:commandButton action="#{basicFlow.serveResponse}" value="Invoke Action" />
    </h:form>

    <h:link outcome="composition1.xhtml">
      Composition 1
    </h:link>
  </h:body>
</html>

值得提醒的是,该文件不是 HTML5 文档;虽然 JSF 2.2 可以应付文档语法,但我们必须先走才能跑。 XHTML 是一种 HTML 格式,它使用 XML 模式命名空间来添加额外的标签。因此,存在用于 HTML、UI 和 F 的特定于 JSF 的名称空间。有关这些名称空间的描述,请参见下文。

<h:head><h:body>< h:form> 自定义标签类似于 Web 开发中每个人都知道的标准 HTML 元素标签。这是因为它们旨在刻意反映这一目的。实际上,这些是添加功能并最终呈现等效 HTML 元素输出的 JSF 自定义标记。

您可能想知道地球是什么<h:link> 元素。这只是 JSF 呈现 HTML 锚标记的方式。结果标记属性直接引用另一个 XHTML,在 JSF 2.0 之后,允许开发人员在代码中编写它。

<h:commandButton> 标记是最终呈现 HTML 提交元素标记的 JSF 表单按钮的示例。此标记接受引用特殊字符串的操作属性。字符串是表达式语言的一个例子;它引用了一个bean的方法名。

下面是 JSF 托管 bean 的代码,BasicFlow

package uk.co.xenonique.digital.javaee.jsfcomps;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class BasicFlow {
    public String serveResponse() {
        return "endState.xhtml";
    }
}

BasicFlow 是一个 CDI bean,其请求范围生命周期由 @javax.enterprise.context.RequestScoped 注释声明。 bean 由 CDI 框架在 servlet 请求生命周期开始时创建,并在 servlet 响应完成后完成并留给垃圾收集。

在 JSF 2.2 中,我们将使用 @javax.inject.Named 注释来指定 JSF 框架可用的 bean。我们可以将注解显式写成@Named("basicFlow"),但默认是简单类名的驼峰式标识符。我们建议数字开发人员不要使用旧的 @javax.faces.bean.ManagedBean 注释,因为它现在将在未来的 JSF 规范中被弃用。

Tip

确保您的 POJO 实际上是 CDI bean。如果您对 JSF 使用了错误的导入,那么 JSF 可能会出现很多混乱。在部署时,您将无法在 #{basicFlow.serveResponse} 之类的表达式中注入或找到支持 bean。检查您是否正在导入 javax.enterprise.context.RequestScoped 而不是导入已弃用的 javax.faces.bean.RequestScoped 注释。

#{basicFlow.serveResponse} 字符串是 Expression LanguageEL),这是一种页面内容与支持 bean 通信同时维护 < /a>关注点分离。第一个 BasicFlow 元素将 指向支持 bean 实例,第二个 serverResponse 元素指的是 serveResponse() 方法。因此,这是一个引用支持 bean 方法的 EL 表达式。我们将在本章后面学习更多关于表达式语言的知识。

可以看到响应是一个简单的字符串,也就是下一个 VDL 文件:endState.xhtml。严格来说,后缀可以省略,JSF 框架将确定正确的视图。

endState.xhtml Facelet 视图文件如下所示:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html">
  <h:head>
    <title>Digital Java EE 7 - End State</title>
  </h:head>
  <h:body>
    <p>
      This is the <strong>end state</strong>.
    </p>
    <h:link outcome="index.xhtml">
      Go back to the start
    </h:link>
  </h:body>
</html>

这是一个 JSF 视图,它允许用户使用 <h:link> 元素返回到开始视图。

A web deployment descriptor

为了充分利用 JSF 框架,我们建议配置一个Web 应用程序部署描述符。该文件是一个特殊的 XML 文档,它以声明方式描述入口 servlet 端点、servlet 映射和其他环境资源。 XML文件的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="false">

  <display-name>
    jsf-compositions-1.0-SNAPSHOT
  </display-name>

  <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>*.xhtml</url-pattern>
  </servlet-mapping>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>

  <welcome-file-list>
    <welcome-file>index.xhtml</welcome-file>
  </welcome-file-list>
</web-app>

前面的文件有 WEB-INF/web.xml 路径。为了激活 JSF 框架,部署描述符用完全限定的类名声明 servlet; javax.faces.webapp.FacesServlet。请注意,servlet 被映射为提供 *.xhtml 文件。

我们将使用上下文参数 javax.faces.PROJECT_STAGE 和适当的值来定义当前项目的活动阶段。在前面的示例中,阶段设置为 Development 但应用程序上线后,我们可能希望将值切换为 Production代码>。切换到 Production 可以提高性能并禁用 一些调试输出。

您将在本书的源代码中找到 部署描述符,作为项目 ch02/jsf-compositions 的一部分.将项目添加到 IDE(例如 IntelliJ、Eclipse 或 NetBeans)后,您可以在 URL http://localhost:8080/jsf-compositions- 查看应用程序服务器的输出1.0-快照/

JSF XML namespaces

这是一个描述了常见的JSF和相关的命名空间:

命名空间

描述

h

http://xmlns.jcp.org/jsf/html

这定义了 HTML 渲染器和组件的标准标记 JSF 库,例如 h:linkh:commandButton 等等。

f

http://xmlns.jcp.org/jsf/core

这个为独立于任何渲染工具包的核心功能定义了标准标签JSF库。该库包含处理验证和转换的标签。

ui

http://xmlns.jcp.org/jsf/facelet

定义了标准标签JSF 库来模板化支持,包括视图的组成。

cc

http://xmlns.jcp.org/jsf/composite

这个 定义了标准的tag 库来构建复合组件。

jsf

http://xmlns.jcp.org/jsf

定义了支持 HTML5 友好输出的标签。

p

http://xmlns.jcp.org/jsf/passthrough

定义了标签以支持带有传递 tag 属性的 HTML5 友好输出。

c

http://xmlns.jcp.org/jsp/jstl/core

这个为JSP 核心行为定义了JSTL 1.2 标记库。这些标签包括 <c:forEach><c:if>< ;c:choose><c:catch>

fn

http://xmlns.jcp.org/jsp/jstl/ficmtion

这个为JSP 函数定义了JSTL 1.2 标记库。这些标签包括 <fn:upperCase><fn:length> <fn:contains>

必须将诸如 fn 之类的缩写名称添加到根 XML 文档元素中,该元素在 的大部分case 是一个 HTML 元素。

A Composition example


在结束本章之前,让我们深入研究一些代码来演示 JSF 组合。我们将从一个简单的 JSF 模板开始,该模板将网页分为两个区域:顶部区域和主区域。

template-top.xhtml 文件是执行以下操作的 JSF 视图:

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

  <h:head>
    <title>Digital Java EE 7 - Sample JSF</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="XeNoNiQUE"/>
    <meta name="author" content="Peter Pilgrim"/>

      <h:outputStylesheet library="styles" name="main.css" rel="stylesheet"/>
      <h:outputStylesheet name="/styles/top.css" rel="stylesheet"/>
  </h:head>

  <h:body>
    <div id="content">
      <div id="top" class="topContent">
        <ui:insert name="top">
          Reserved for Top Content
        </ui:insert>
      </div>

      <div id="main" class="mainContent">
        <ui:insert name="content">
          Reserved for Main Content
        </ui:insert>
      </div>
    </div>
  </h:body>
</html>

前面的代码是模板母版。到目前为止,一切都很好。这类似于带有 HTML 元素的标准网页,我们可以看到该页面使用嵌套的 DIV 元素来构造内容。我会提请您注意 <h:outputStylesheet> 标签,它表示我们应该包含的几个级联样式表作为资源。

ui:insert 标记是组合 JSF 标记,它表示模板中将被客户端模板中的占位符替换的区域。插入占位符必须有一个名称,在这个例子中我们有两个,即顶部和内容。请注意,ui:insert 标签被插入到 HTML div 元素的正文内容中。

以下是客户端模板的代码 composition1.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" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <ui:composition template="/template-top.xhtml">
    <ui:define name="top">
      <h1>layout composition 1</h1>
    </ui:define>

    <ui:define name="content">
      The is the main content
      <h:form>
        <h:commandButton action="#{basicFlow.serveResponse}" value="Invoke Action" />
      </h:form>
    </ui:define>
  </ui:composition>
</html>

此文件中的关键注释是 <ui:composition> JSF 自定义标记,它引用了正在使用的主模板。模板属性是指文件的路径。

两个 <ui:define> 标记定义名称占位符,其内容替代主模板中的默认内容。在此示例中,占位符是顶部和内容。

这是此过程的屏幕截图。第一个屏幕截图是初始 Facelets 视图,index.xhtml

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

第二个是第二个 Facelets 视图,endState.xhtml

读书笔记《digital-java-ee-7-web-application-development》JavaServer Faces生命周期

JSF serving resources

JSF 期望 我们的 Web 资源默认放置在 resources 文件夹中。快速查看以下文件目录将有助于您理解:

jsf-composition

jsf-composition/src/main/webapp

jsf-composition/src/main/webapp/resources

jsf-composition/src/main/webapp/resources/images

jsf-composition/src/main/webapp/resources/javascripts

jsf-composition/src/main/webapp/resources/styles

jsf-composition/src/main/webapp/WEB-INF

jsf-composition/src/main/webapp/WEB-INF/classes

jsf-composition/src/main/webapp/WEB-INF/lib

在这个 Gradle 项目的简化视图中,我们可以看到放置在 resource 文件夹下的文件夹、图像、JavaScript 和 CSS 文件。让我们再次提醒一下 JSF 视图代码,如下:

<h:outputStylesheet library="styles" name="main.css" rel="stylesheet"/>
<h:outputStylesheet name="/styles/top.css" rel="stylesheet"/>

这些标签本质上是指两个文件:resources/style/top.cssresources/style/main.css

为了使这些 标签起作用,资源必须放在资源文件夹下,或者可以放在 META -INF/resources 与 Web 应用程序一起部署的 Web 应用程序 JAR 文件的文件夹。该规范列出了以下两个选项:

<ROOT>/resources/<RES-ID>

否则,你可以使用这个:

<ROOT>/WEB-INF/lib/<DEPENDANT-JAR>/META-INF/resources/<RES-ID>

这里,<ROOT>是项目web根目录,<DEPENDANT-JAR>是第三方依赖的JAR, <RES-ID> 是资源标识符。

<RES-ID> 可以进一步分解成正式的部分,如下:

<RES-ID> ::=
   [ <LOCALE-PREFIX> / ] + 
   [ <LIBRARY-NAME> / ] [ <LIBRARY-VERSION> / ] +
   <RESOURCE-NAME> [ / <RESOURCE-VERSION> ]

[] 中的术语部分是可选的,但资源名称除外。因此,可以在库中拥有完全国际化、版本化和模块化的资源标识符。也许您的项目可能会利用以下资源:

/en_us/sportsBulletin/v1_2_3/top_masters_golfer/2015_may.png

如果您想逆势而上并更改 JSF 2.2 中资源的默认位置会发生什么?可以在 web.xml 部署描述符文件中配置替代文件夹。您可以设置上下文参数变量:javax.faces.WEBAPP_RESOURCES_DIRECTORY

这是将资源文件夹定义为资产的描述符的摘录:

  <context-param>
    <param-name>
    javax.faces.WEBAPP_RESOURCES_DIRECTORY</param-name>
    <param-value>assets</param-value>
  </context-param>

我们将在 Chapter 3, 构建 JSF 表单第 4 章 JSF 验证和 AJAX

Expression language


表达式语言版本 3.0 现在是 Java EE 7 中的一个单独规范。它允许 页面作者引用 bean 属性、调用方法和执行算术运算在页面描述语言中。最初,EL 是 JavaServer Pages 规范的一部分,它也对 JSF 技术有用。 EL 有两种类型:立即评估和延迟评估。 JSP 依赖于立即形式,而 JSF——因为它对请求和响应的生命周期管理——需要延迟形式。 EL 3.0 代表了这些策略的 最终统一。

EL 对页面作者很有用,因此数字开发人员可以引用托管 bean 中的属性、CDI 组件和控制器中的引用方法。 EL 还用于调用控制器中的方法。 EL 用于从 bean 中检索值,但它也可以设置值。

Immediate and deferred expressions

表达式的语法有两种形式:${expression}#{expression}。开头的美元 字符表示立即计算的表达式。开头的哈希字符表示稍后延迟的表达式

立即表达式 总是被立即计算,作为一个结果,它们只能用于读取值而不是设置它们。在呈现页面时进行评估,这通常是 JavaServer Pages 的情况。

当我们使用 JSF 构建应用程序时,我们将在本书中主要使用延迟表达式。延迟表达式意味着幕后的技术负责获取文字文本并将其解释为结果或调用返回结果的函数。

让我们快速向前一点,介绍一个名为 <h:inputText> 的 JSF 自定义标记。此标记呈现一个 HTML 输入元素,其类型为文本。该标签接受一个 id 属性来指定一个 HTML 标识符;但是,我想提请您注意 value 属性,它指定了一个 EL 值表达式。

我们可以编写一个像在 JSP 中一样立即计算的值表达式。这是代码片段:

<h:inputText id="firstName" value="${employee.firstName}"/>

这将作为只读值工作。但是,无法使用 JSF 将该值应用到员工 bean,因为在任何生命周期状态下都不会计算表达式。

如果我们将表达式从立即形式更改为延迟形式,那么我们将看到以下行为:

<h:inputText id="firstName" value="#{employee.firstName}"/>

通过此更改,EL 会在生命周期的渲染响应阶段立即进行评估。 JSF 实现执行评估并从名为employee 的bean 中检索firstName 属性的值。

表单作为 Faces 请求回传到服务器时,通过方式也是称为postback事件,JSF实现有一个有机会在延迟时间检索值。正是在这些后期的生命周期状态——应用请求值、流程验证和更新模型——评估值表达式并将来自 Faces 请求的值注入到目标 bean 属性中。

Value expressions

值表达式 是返回单个结果的表达式。 值是从管理 Java 实例集合的实现中的对象图中检索的。对于 Java EE,它是 JSF 或 JSP 提供程序,而对于应用程序服务器,它是一个上下文和依赖注入提供程序。 CDI 在 @javax.inject.Named bean 内部维护一组集合。 (请等待命名bean的解释或直接前往第3章构建 JSF 表单。)特别是,JSF 传统上会记录使用 @javax.faces.bean.ManagedBean 注释的托管 bean。

JSP 会在页面范围、请求范围、会话范围中搜索命名对象,然后是 servlet 容器的应用范围。在幕后,有一个抽象类javax.el.E​​LResolver的子类,它负责求值。这个类有有用的方法,例如getValue(), setValue(), isReadOnly( )invoke(),开发人员可以使用它们以编程方式向自己的应用程序添加评估。

在任何情况下,值表达式的第一个调用端口是具有标识名称的对象实例。这被称为初始期限。之后,评估逻辑可以使用点符号 (.) 通过命名属性遍历对象图。评估继续通过表达式中的后续项。让我们暂时使用 JSF,并考虑 #{employee.firstName} 表达式将评估对范围内名为employee 的对象的延迟搜索。然后 EL 解析器将在名为 firstName 的 bean 中查找属性,这反过来将调用 getFirstName() 方法。作业将完成,EL 解析器返回属性的结果值。

EL 也适用于 Java 集合。特别是,java.util.Map 集合被特殊处理。标准 EL 假定集合具有 String 类型的键,我们可以将其视为 Map 。可以使用点表示法或方括号表示法[] 访问 Map 中的条目。

下面的值表达式表将使这个方案对于更复杂的表达式更加清晰:

表达

意义

员工

这会找到与名称 employee 关联的初始术语

employee.firstName

这将解析命名实例并调用 getFirstName()

employee.department.name

这将解析对象,调用 getFirstName(),检索下一个对象,并在该对象上调用 getName()

员工["firstName"]

这相当于点符号 employee.firstName

员工['firstName']

这相当于点符号 employee.firstName

capitalCities['Brazil']

这会找到名称实例,并假设 capitalCitiesjava.util.Map 的类型,并使用键检索值巴西

capitalCities["Brazil"]

这是一个等价于前面的映射表达式

方括号表示法 [] 对于包含破折号和/或句点字符的字符串非常有用。当您想要从资源包中提取消息以用于 国际化目的时,此表示法会有所帮助。您可以将 值表达式编写为:appMessages["registeredTraveller.applicant.firstName.required"]

方括号表示法允许我们编写特殊的表达式。我们可以写出如下的值表达式:

${employee['class'].simpleName}

这将转换为以下等效的 Java 代码:

employee.getClass().getSimpleName()

Map expressions

EL 使用方括号 括号符号地图对象“字面量”>[]。如果表达式计算为访问或读取与右侧的 Map 键关联的值的引用(rvalue),则 EL 解析器将转换为 Map.get("key") 调用。以下是读取值的表达式:

${capitalCitiesMap['France']}   
  // translates to capitalCitiesMap.get("France")
#{capitialCitesMap['France']}  // ditto, but deferred

如果表达式 绑定到左侧(一个lvalue),那么 EL 解析器转换为 Map.put("key", newValue)

List expressions

EL 可以 使用 方括号表示法从索引数组中检索对象。它的工作原理与 Map 表达式完全一样,只是键必须计算为文字整数。在 EL 中,数组索引号从零开始,正如预期的那样。

因此,如果 departmentListjava.util.ListdepartmentArray 是一个原始数组:

${departmentList[0].name}
${departmentArray[0].name}

这些是等效的伪 Java 语句:

List<Department> department = resolve(...)
departmentList.get(0).getName()

Department departmentArray[] = resolve(...)
departmentArray[0].getName()

Resolving the initial term

EL 依赖 从 servlet 容器、托管 bean 的 JSF 列表和具有命名 bean 的 CDI 范围中查找初始术语的能力。本质上,您可以为 JSF bean 指定任何您想要的名称,但您应该避免使用预定义的对象。初始项是表达式的第一部分。

在 servlet 容器中,您可以引用几个预定义的对象。例如,requestScope 是页面上所有请求范围属性的映射集合。该请求也是 EL 中的一个预定义对象,它表示传递给 JSF 视图的 javax.servlet.http.HttpServletRequest 实例。我们可以使用它在 lvalue 表达式中检索 Web 应用程序上下文路径,如下所示:

<link href="#{request.contextPath}/resources/styles/main.css" rel="stylesheet"/>

前面的代码对于确保可以在 JSF 应用程序中找到资源非常有帮助。它用于创建可靠的相对 URL。我们将在Chapter 4JSF Validation and AJAX中详细解释/跨度>。

初始项的解析首先检查表达式中的初始项是否为预定义对象。如果它是一个预定义的对象,那么解析器会继续这个对象。如果不是,则 JSF 实现按以下顺序在 servlet 容器范围之一中搜索对象名称:requestScope, sessionScope applicationScope

如果没有按名称找到对象,JSF 2.2 框架将委托给 ELResolver,后者将为实例搜索等效的 CDI 范围,然后查看已注册的 或带注释的托管 bean。

下表 列出了表达式语言中的预定义对象实例:

预定义名称

描述

applicationScope

是应用程序范围属性的 Map 集合(javax.servlet.ServletContext.getAttributes()

应用程序

这个 指的是 ServletContext 实例

cookie

这是 cookie 名称和值的 Map 集合

facesContext

这是这个页面和生命周期的javax.faces.context.FacesContext实例

标题

这是 HTTP 标头参数的 Map 集合,仅产生多个值的第一个元素

headerValues

这是一个 HTTP 标头参数的映射集合,产生一个 String[] 值数组

initParam

这个是Web应用初始化参数的Map集合

参数

这是 一个 HTTP 请求参数的 Map 集合,只有任何值数组中的第一个元素

paramValue

这是 HTTP 请求参数的 Map 集合,产生 String[] 值数组

requestScope

这个 是请求范围属性的 Map 集合(HttpServletRequest.getAttributes()

请求

这个 指的是 HttpServletRequest 实例

sessionScope

这是 会话范围属性的 Map 集合(HttpSession.getAttributes()

会话

这个 指的是 HttpSession 实例

查看

这个 是页面的javax.faces.component.UIViewRoot 实例

让我们继续讨论方法表达式。

Method expressions

EL 还允许 关联到对象实例上的方法。这种类型的引用称为方法绑定表达式。 JSF 框架允许方法表达式引用操作方法、验证器、转换器和阶段侦听器。方法表达式调用命名对象实例上的方法,然后返回结果(如果有)。

方法表达式的一个很好的示例是托管bean 上的操作处理程序,您已经在本章的基本JSF 示例中看到了这一点。

<h:commandButton action="#{basicFlow.serveResponse}" value="Invoke Action" />

#{basicFlow.serverResponse} 表达式是一个方法绑定,它引用控制器、名为 BasicFlow 的 CDI bean,以及serveResponse() 方法。

Parameterized method expressions

EL 还 支持带参数的方法调用。参数可以是文字常量。它们也可以是页面范围内的术语名称。这提供了一种非常强大的方式来构建具有列表集合和其他复杂数据结构的应用程序。

以下是使用方法参数代码的表达式示例:

<h:inputText action="#{complexFlow.process('SALE',productLine)}" value="Purchase Products/>

process() 方法在使用 complexFlow 初始项解析的对象实例上调用。第一个参数是文字字符串。第二个参数是 subtermproductLine 的值,我们假设它可用于 EL 解析器。

也可以通过定义获取集合的大小,因为这是一个无参数的 调用。此表达式 看起来像 #{genericSearchResult.size()},假设初始术语引用一个类型java.util.Collectionjava.util.Map

Arithmetic expressions

我们可以使用算术运算符在表达式中执行计算。表达式还可以包含关系和逻辑运算符。

在 EL 中,以下是保留运算符:

  • 算术运算符:+ - * / div % mod

  • 关系运算符:==eq!=ne, <lt, > gt, <=le, < code class="literal">>= 或 ge

  • 逻辑运算符:&& and, || or, ! 不是

  • 空运算符:空

以下是一些使用中的算术表达式的示例:

<p> The expression \#{1+5*2-3/4} evaluates to: #{1+5*2-3/4} </p>
<p> The expression \#{(2014 div 4) mod 3} evaluates to: #{(2014 div 4) mod 3} </p>
<p> The expression \#{2018 lt 2022} evaluates to: #{2018 lt 2022} </p>
<p> The expression \#{0.75 == 3/4} evaluates to: #{0.75 == 3/4} </p>

请注意转义字符的使用——反斜杠 (\),它会阻止 JSF 视图解释表达式。我们还可以直接在页面上呈现表达式,而不需要 <h:outputText/> 自定义标签。这对页面作者来说是一种很好的享受。

Tip

保留 MVC 模型

最好将业务逻辑放在控制器 bean 中,而不是用复杂的条件填充页面。

Page navigation


在 JSF 2 之后,在控制器中提供导航非常。在早期 JSF 示例的 BasicFlow 控制器中,我们依赖于隐式页面导航。开发人员可以通过返回一个简单的字符串来指定要呈现的下一页。

这是控制器类:

@Named @RequestScoped
public class BasicFlow {
    public String serveResponse() {
        return "endState.xhtml";
    }
}

在 JSF 1 中,页面导航是在 Faces Configuration XML 文件中显式确定的:/WEB-INF/faces-config.xml,由于强制认知,这使得开发更加困难间接性。 faces-config.xml 的目的是为 JSF Web 应用程序定义配置。开发人员可以定义导航规则、注入 bean 属性、定义属性文件以及声明资源包和语言环境。他们可以注册转换器、验证器和渲染器组件。

显式页面导航对于定义的信息架构路径很有用。编写页面导航更容易在团队中共享。为业务利益相关者制作原型可能非常快。但是,如果您的控制器和呈现的页面直接以一对一的关系映射,则显式导航可能是多余的。

The navigation rules

JSF 还支持 Faces 配置 XML 文件中的显式导航规则。我应该警告您,这是 JSF 1 中的旧样式。x 明确描述页面之间的导航。在 JSF 2.x 中不再需要导航规则,如果你想更好地描述页面导航,提醒自己学习 Faces Flows (参见 第 6 章JSF 流程和技巧 )。但是,在您的专业工作中,您很有可能会遇到较旧的 JSF 应用程序,因此,您需要了解 JSF 导航规则是如何设计的。

考虑到这一点,以下是导航的明确工作方式。假设我们有一个简单的页面,我们可以在其中选择一组页面中的水果和蔬菜集合。我们也可以选择取消选择。

以下是标准 Faces 配置文件 faces-config.xml 中这些规则的表示。该文件通常位于 Maven 和 Gradle 项目的 src/main/webapp/WEB-INF 中:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd" version="2.2">
  <navigaton-rule>
    <from-view-id>/order-start.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>cancel</from-outcome>
      <to-view-id>/cancel.xhtml</to-view-id>
    </navigation-case>
  <navigation-case>
      <from-outcome>fruit</from-outcome>
      <to-view-id>/fruit.xhtml</to-view-id>
    </navigation-case>
  <navigation-case>
      <from-outcome>vegetable</from-outcome>
      <to-view-id>/vegetable.xhtml</to-view-id>
    </navigation-case>
  </navigation-case>
</faces-config>

显式导航由 JSF 框架应用的一组规则确定。开发人员将在 navigation-rule> 标签中编写一系列复合元素。规则的上下文由 <from-view-id> 元素确定,该元素引用特定的视图页面 /order-start .xhtml,或者它可以是适用于多个导航案例的通配符规则(星号 *)。每个导航规则都有一个 元素的集合。每个案例都需要一个 <from-outcome><to-view-id> 元素。结果标识了由控制器方法返回的文字字符串,视图 ID 是目标视图。因此,在取消情况下,由取消标识的结果字符串将导航到 /cancel.xhtml 视图。

结果间接映射到视图页面的优势是显而易见的。控制器中的结果代码保持不变,但目标视图可以更改。

我们可以编写一个 JSF 控制器来处理这些导航规则。这是 ProductTypeController 类的摘录:

@Named @ViewScoped
public class ProductTypeController {
  public String productType;
  /* ... getter and setter omitted */

  public String cancel() { return "cancel";}
  public String navigate() {
    if ("fruit".equalsIgnoreCase(productType)) {
      return "fruit";
    }
    else {
      return "vegetable";
    }
  }
}

cancel() 方法 只返回取消结果,JSF 映射到 /cancel.xhtml 页面,因为导航案例与结果匹配。 navigate() 方法根据 productType 属性设置结果。 fruitvegetable 方法只能有两个结果,导航案例确保 fruit.xhtmlvegetable.xhtml 页面分别呈现。

Wildcards

导航规则 也可能有通配符视图。通配符导航规则出现在星号 (*) 字符添加到 <from-view-id> 元素。

假设我们有一个 网站,其中包含受保护的页面,除非用户以注册用户身份登录,否则这些页面不应显示。我们可以编写一个导航规则,供保护区下的所有页面共享。假设我们要保护 URI /secured 下的任何网页:

<faces-config ...>
  <navigaton-rule>
    <from-view-id>/secured/*</from-view-id>
    <navigation-case>
      <from-outcome>login</from-outcome>
      <to-view-id>/login.xhtml</to-view-id>
    </navigation-case>
    <navigation-case>
      <from-outcome>register</from-outcome>
      <to-view-id>/register.xhtml</to-view-id>
    </navigation-case>
  </navigation-case>
  ... more rules ...
</faces-config>

通配符 from-view-id /secured/* 标识所有以 /secured/ 前缀开头的页面。 URI 路径中只能有一个通配符。

在结果中使用通配符会引发优先级问题。通配符 view id 何时优先于直接结果?这是 Faces Configuration XML 的导航案例摘录,我们在其中设置了源页面视图:

<from-view-id>stocks.xhtml</from-view-id>
// Has higher precedure than 
<from-view-id>*</from-view-id>

直接 view id 总是比等效的通配符视图具有更高的优先级。 JSF 在通配符视图上选择直接视图的导航案例 stocks.xhtml,如下所示:

<from-view-id>/secured/portfolio/*</from-view-id>
// Has higher precedure than 
<from-view-id>/secured/*</from-view-id>

如果有多个 通配符视图在竞争 匹配项,则选择最长的匹配。 JSF 在最长匹配视图中选择导航用例,在插图 /secured/portfolio/* 中。

Conditional navigation

JSF 显式页面 导航也支持Faces 配置中的条件导航的思想文件。这允许开发人员根据应用程序的动态状态以声明方式设置导航规则。条件导航是通过使用 <if> 元素标签实现的,如下例所示:

<faces-config>
  <navigaton-rule>
    <from-view-id>/shopping-cart.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>nextPage</from-outcome>
      <if>#{user.registered}</if>
      <to-view-id>/existing-customer.xhtml</to-view-id>
    </navigation-case>
    <navigation-case>
      <from-outcome>nextPage</from-outcome>
      <if>#{!user.registered}</if>
      <to-view-id>/new-customer.xhtml</to-view-id>
    </navigation-case>
  </navigation-case>
</faces-config>

<if> 的正文内容是一个延迟值表达式,它在 JSF 生命周期中进行评估,应该返回一个布尔值。在代码中,#{user.registered} 表达式被评估为具有当前登录用户配置文件 bean 和名为已注册的属性的 bean。 #{!user.registered} 表达式计算否定 - 请注意对运算符使用感叹号。

Static navigation

要完成关于页面导航的 数字开发者故事,是时候看看使用 JSF 的静态导航了。静态导航允许我们在不调用托管 bean 控制器的情况下从一个 JSF 页面遍历到另一个页面。它对于不需要服务器端 Java 代码或没有 HTML 输入元素的页面视图很有用。静态导航是通过 Facelets 视图上的标记和显式导航规则的组合来实现的。

在前面的 Basic JSF 示例中,我们有一个带有 <h:link> 的页面视图。让我们把它改成 <h:commandButton>

<h:form>
  ...
  <h:commandButton action="your-next-view">
      Composition 1
  </h:commandButton>
</h:form>

action 属性指定要导航的结果的名称。我们将用 <h:commandButton> JSF 标记替换旧元素。 action 属性指定值表达式。 JSF 在多个上下文中查找初始术语,但它也会搜索 Faces 配置以匹配导航规则。为了使这种遍历工作,我们还需要在 Faces 配置中使用导航规则:

<navigation-case>
  <from-outcome>your-next-view</from-outcome>
  <to-view-id>/shopping-cart.xhtml</to-view-id>
</navigation-case>

导航规则与 Facelets 视图中的 your-next-view 结果相匹配,因此 JSF 可以导航到目标页面。

我想我们将在这里停止关于页面导航主题。我们将在 Chapter 4JSF 验证和AJAX

Summary


本章是 JSF 世界中的一次强有力的冒险。您应该能够理解框架是如何组合在一起的理论。我们介绍了 JSF 的关键特性,例如 HTML5 友好的标记和模板引擎。 JSF 是 Java EE 平台的一部分,可用于许多应用程序服务器和 servlet 容器。我们了解了 JSF 框架如何与模型-视图-控制器设计模式相关联。

您应该能够理解执行和渲染生命周期下的 JSF 中的请求和响应处理生命周期以及相变状态模型。

在中间章节,我们检查了 JSF 基本页面、自定义标记库、Facelets 视图和一个简单的支持 bean。我们还观察了主模板和客户端模板的组合布局。

我们还详细介绍了强大的 EL 框架,它是 Java EE 7 和 JSF 2.2 的一部分。 EL 是服务器端 Java 应用程序的一个非常重要的工具,尤其是当它们是针对 Faces 构建时。为了完成旅程,我们查看了隐式和显式页面导航。

我们现在有足够的知识来构建 JSF 基础。在下一章中,我们将着眼于通过 HTML 表单增强我们的 JSF 知识,并为验证奠定基础。在接下来的章节中,我们肯定会丢掉 XHTML 文档,添加 HTML5,这样我们就可以开发更现代的网站。

Exercises


以下是本章的问题:

  1. 在计算机科学的其他什么地方可以找到模型-视图-控制器设计模式?

  2. 为什么您认为敏锐的计算机科学家和架构师想要将业务逻辑与表示视图代码分开?

  3. 考虑一种情况,您与市政府签订了当地领土的合同。

  4. 您被要求为选民编写一个选民名册网络应用程序,以取代传统的纸质记录。公民无需向公民发送正式信件并等待收到填写的表格,而是可以在线登记选民名册。这个应用程序中的模型-视图-控制器是由什么构成的?

  5. JSF 生命周期的哪些部分映射到模型-视图-控制器模式?

  6. 描述框架将在何时何地遇到恢复视图阶段。

  7. 描述 HTML 表单提交的过程。在 JSF 中将 HTML 表单的内容传输到 Java POJOS 会发生什么?

  8. 当客户在表单中输入无效值时,请描述将处理 Faces 请求的 JSF 生命周期的各个阶段。您认为 Faces 响应中添加了什么?为什么?

  9. 为什么 JSF 规范编写者明确设计了一个特殊的渲染响应阶段?

  10. JSF 已经明确地将评估与支持 bean(或动作控制器)中的业务逻辑调用分开。其他 web 框架在支持 bean 中有一个验证代码。概述这两种方法的优缺点。

  11. 下载第 2 章的代码并研究 Web 应用程序的布局。如果您感到勇敢,请修改其中一个项目示例。将另一个字符串属性添加到支持 bean,然后添加一个 JSF 表单文本字段(提示:<f:inputText>)。怎么了?如果您的更改出错,您可以随时还原更改。