《Java EE 7精粹》—— 3.10 Faces Flow

3.10 Faces Flow

JSF2.2引入了Faces Flow。这个功能借用了ADF Task Flows、Spring Web Flow和Apache MyFaces CODI的核心概念,使其标准化为JSF2.2的一部分,为在应用程序中定义控制流程,提供一种模块化的方法。

Faces Flow将相关网页和相应的后台Bean封装成一个模块。该模块具有定义良好的入口点和出口点,可以由应用程序开发人员来分配。通常在Faces Flow中的对象被设计为允许用户来完成的任务,该任务需要在若干不同视图输入信息。应用程序也因此成为流而不仅仅是视图的集合。

试想有一个多页面的购物车,第一个页面用于选择项目,第二个页面用于选择配送,第三个页面用于输入信用卡详细信息,第四个页面用于确认订单。可以使用托管Bean去捕获数据,会话作用域的变量在页面之间传递信息,点击按钮来调用后台EJB的业务逻辑,以及从一个页面到另一个页面的(有条件的)导航规则。这样的实现会有如下问题。

  • 这种流的顺序通常是一个更大的应用程序的一部分。该应用程序通常有很多页面,是一个巨大的流而且一切都是全局可见的,无法逻辑分区。
  • 页面或视图的流不能被封装为一个逻辑单元,因此不能被重复使用,不容易并入另一个更大的应用程序或流。
  • 同样的流不能在多个窗口打开,因为会话作用域变量是在页面之间传递信息的。CDI定义了@ConversationScoped,但那只是解决方案的一部分。
  • 基于请求作用域服务于特定的请求,基于会话作用域大于请求作用域,但是在浏览器关闭后会无效。我们需要一种作用域可以跨越每个应用逻辑的多个页面,这正是所缺的。
  • 最小的逻辑粒度是页面。调用应用逻辑的唯一方法就是把它绑到页面中的由用户激活的UI组件。在没有任何用户启动的操作之前,业务逻辑不能被调用。

Faces Flow提供了以下针对这些问题的解决方案。

  • 应用程序被分解成一系列模块化的流,他们之间可以相互调用。
  • 这些页面的流可以打包为模块,可以在相同或完全不同的应用程序中被重用。
  • 共享内存作用域(例如,流作用域)允许数据在task流中的视图之间传递。
  • 可以在Bean中指定新的CDI作用域@FlowScoped。这使得在进入/退出作用域时,自动激活/去活Bean。
  • 业务逻辑可以从基于流定义的页面中的任何地方被调用。

应用程序的流不再局限于在页面之间流动,而是被定义为在“节点”之间流动。有以下五种不同类型的节点。

  • 视图(View):应用程序中的任何JSF页面。
  • 方法调用(Method call):通过一个EL表达式从流图调用应用逻辑。
  • 切换(Switch):导航决定基于布尔EL表达式的流图。
  • 流调用(Flow call):通过参数调用另一个流并接收返回值。
  • 流返回(Flow return):返回到调用流。

这些节点定义流的入口点和出口点。

新引入的CDI作用域@FlowScoped定义了指定流中Bean的作用域。这使得当进入/退出作用域时,自动激活/去活Bean:

在这段代码中,这个Bean有两个流作用域的变量:address和creditCard。Bean定义的流为flow1。

还引入新的流存储EL对象,#{flowScope}。该流存储对象映射到facesContext.getApplication().getFlowHandler().getCurrentFlowScope():

在这段代码中,文本框中输入的值和#{flowScope.value}绑定,这个EL表达式的值能够被流中的其他页面访问。

可以使用以声明方式定义流,或使用FlowBuilder API以编程方式定义流。这两种机制是互斥的。

流可以打包在JAR文件或目录中。JAR包要求流在JAR文件中的META-INF/faces-config.xml中显式地声明。流节点封装在META-INF/flows/ ,其中是一个JAR目录项,名字是对应的FlowDefinition类中指定的流id。如果@FlowScoped Bean和通过FlowBuilder定义的流被打包在JAR文件中,他们必须附有META-INF/beans.xml:

在这个JAR包中:

  • 有flow1和flow2两个流。
  • META-INF/beans.xml需要启用CDI。如果Bean定义的注解在归档中使用,可能隐式启用。
  • MyFlow1Bean和MyFlow2Bean是流作用域的Bean。这些Bean被用于存储任何流的本地数据。
  • MyFlow1Definition定义了一个流的入口点和出口点,从另一个流进来的参数名称和值,到另一个流去的参数名和值,以及到其他节点的导航:

    在这段代码中:

    • 流是通过CDI生产者以编程方式定义的。@FlowDefinition是一个类级别的注解,使流通过流畅的流生成器API来定义。
    • 一个FlowBuilder实例是通过@FlowBuilderParameter注入作为参数,用于定义流。
    • flow1被定义为流标识符,flow1.xhtml被标记为起始节点。
    • returnNode方法用于定义流的出口点。在本例中,流由goHome动作定位到/index。节点值可以被指定为一个EL表达式,例如,“可以被绑定到一个Bean”。
    • 命名为传入的参数是来自另一个流命名参数,通过inboundParameter方法定义。该方法的值使用对应的outboundParameter元素填充。该值通过#{flowScope}存储在流本地存储。
    • flowCallNode方法用于定义流的出口点。在本例中,flow2流被调用。命名为传出的参数及其值是通过outboundParameter方法设置的。

    同样地,MyFlow2Definition类定义flow2。

    流打包在目录中,使用约定优于配置。这些约定是:

    • 每个视图声明语言文件,由.xhtml页面定义,在该目录中是该流的视图节点。
    • 流的起始节点是与流相同名称的视图。
    • 在目录中的任何视图之间的导航被认为是在流中。
    • 导航到该目录以外的视图被认为是一个流的出口点。
    • 可选的-flow.xml文件代表流的定义。在这个文件中的规则会覆盖刚才所描述的约定:

      遵循方才定义的约定,在这个目录中:

      • flow1.xhtml是流flow1的起始节点,flow1a和flow1b是同一流中的另外两个节点。
      • flow2.xhtml是流flow2的起始节点,flow2a和flow2b是同一流中的另外两个节点。flow2/flow2-flow.xml定义了流flow2的声明式导航。定义了流的入口点和出口点,来自另一个流的输入参数名称和值,去向另一个流的输出参数名称和值,和到其他节点的导航:

        在这段片段中:

        • 通过id属性为flow1定义了流。
        • 定义了流的出口点。在本例中,流由goHome动作定位到/index。节点值可以被指定为一个EL表达式,例如,“可以被绑定到一个Bean”。
        • 定义了传入参数的名称,该方法的值使用相应的元素填充,并通过#{flowScope}存储在流本地存储。
        • 定义了流的出口点。在本例中,flow2流被调用。定义了传出参数的名称及其值。

        WEB-INF目录将包含页面所需的其他资源,比如backing bean。

时间: 2024-09-20 12:12:35

《Java EE 7精粹》—— 3.10 Faces Flow的相关文章

《Java EE 7精粹》—— 导读

前言 Java EE 7平台建立在以前的版本之上,侧重于提高生产力和拥抱HTML5.本书面向的是想快速概览这个平台和回来复习基础知识的读者. 本书提供了Java EE 7平台中关键规范的概述(每章一个规范).本书绝不是一本介绍不同规范中每一个概念的详尽指南或教程,而是通过简单的代码示例来解释不同规范的主要概念.阅读本书,无需具备Java EE平台早期版本的知识,但你需要对Java有一些基本了解,以便理解代码. 这本书的重要部分来自<Java EE 6 Pocket Guide>(O'Reill

《Java EE 7精粹》—— 1.2 可交付成果

1.2 可交付成果 Java EE 7平台是遵从JCP 2.9作为JSR 342而开发的.JCP过程对每一个JSR定义了以下三个主要可交付成果. 1.2.1 规范(Specification) 描述了被提议的组件及其功能的一份正式的文件. 1.2.2 参考实现(RI) 对被提议的规范的二进制实现.参考实现有助于确保被提议的规范能以一个二进制形式实现,并为规范过程提供不断的反馈. Java EE的参考实现建立在GlassFish社区中. 1.2.3 技术兼容包(TCK) 用于验证参考实现是否符合被

《Java EE 7精粹》—— 2.10 Handling Multipart Requests

2.10 Handling Multipart Requests 可以在Servlet中定义@MultipartConfig注解,表示它期待的请求类型是multipart/form-data.HttpServletRequest.getParts()方法和HttpServletRequest.getPart()方法提供multipart请求的各个部分: 在这段代码中: @MultipartConfig注解定义在类上,表明doPost()方法将接收multipart/form-data类型的请求.

《Java EE 7精粹》—— 第3章 JSF 3.1 Facelets

第3章 JSF JSF是基于Java的Web应用程序开发的服务器端用户界面(UI)框架.使用JSF可以实现: 使用遵循模型-视图-控制器(MVC)设计模式的可重用的UI组件集合来创建网页. 绑定组件到服务器端模型,允许UI与应用程序数据双向移动. 处理UI事件响应和模型交互中的页面导航. 管理跨服务器请求的UI组件状态. 提供从客户端生成的事件到服务器端应用程序代码的简单模型. 轻松地构建和重用自定义UI组件. JSF应用程序包括: 置于UI组件的布局中的一组网页. 一组托管的Bean,包括绑定

《Java EE 7精粹》—— 3.5 Ajax

3.5 Ajax JSF原生支持添加Ajax功能到网页.JSF允许局部视图的处理,其中只有视图中的一些组件用于处理该响应.JSF可以摘选页面上的某些组件,渲染局部页面,而不是整个页面. 有以下两种方法可以启用这种支持. 以编程方式使用JavaScript资源. 以声明的方式使用f:ajax组件. 编程方式的Ajax集成是通过资源处理机制启用的.jsf.js是在javax.faces库中预定义的资源.该资源包含用于Ajax和JSF页面交互的JavaScript API.可以在页面中使用output

《Java EE 7精粹》—— 3.7 服务器和客户端扩展点

3.7 服务器和客户端扩展点 转换器.验证器和监听器是服务器端的附属对象,用于为页面上的组件添加更多的功能.行为是客户端的扩展点,可以使用行为定义的脚本增强组件的渲染内容. 3.7.1 转换器 转换器用于把组件中输入的数据从一种格式转换为另一种格式(例如,string到number).JSF提供了几种内置的转换器,如f:convertNumber和f:convertDateTime.他们可应用于任何可编辑的组件: 在这段代码中,文本框中输入的文本将被转换为一个整数,如果文本不能转换,则抛出一条错

《Java EE 7精粹》—— 3.8 验证数据

3.8 验证数据 除了使用JSF内置的验证器和创建自定义的验证器,还可以使用Bean验证为一个backing bean定义指定的约束. 试想在一个简单的Web应用程序中有一个页面,其表单内包含多个文本字段: 假设每个文本字段绑定到一个托管Bean的属性,且该属性至少有一个相关的Bean验证约束注解: 每个h:inputText元素是由一个UIInput组件支持的,包含一个验证器实例,它的id和javax.faces.Bean相关联.与用户指定的验证约束关联的验证器的validate方法,在处理验

《Java EE 7精粹》—— 3.3 复合组件

3.3 复合组件 JSF使用Facelets的功能和资源的处理来定义复合组件,定义在Facelets标记文件中的一个或多个JSF组件组成了一个复合组件.这个.xhtml文件存储在资源库中,可以从页面的任意区域创建一个可重用的组件. 复合组件在定义页面中定义,在使用页面中使用.定义页面使用定义元数据(或参数),使用<cc:implementation>定义实现,其中cc是http://xmlns.jcp.org/jsf/composite/命名空间的前缀.JSF规范的未来版本可能会放宽对指定元数

《Java EE 7精粹》—— 3.11 资源库契约

3.11 资源库契约 JSF2引入了Facelets作为默认的视图声明语言(VDL).Facelets允许使用XHTML和CSS创建模板,模板可以为应用程序的不同页面提供一致的界面外观.JSF2.2定义了资源库契约,一种与资源关联的模板库,可以以可重用和可互换的方式应用到整个应用程序.应用程序中可配置的视图集合将可以声明自己是资源库契约中任何模板的模板客户端. 资源库契约驻留在Web应用程序的根目录的契约目录中: 在这段代码中: 应用程序同样有两个页面:index.xhtml和new/index