JavaServer Faces 2.2 的新特性:viewAction 组件

作者:Tom McGinn

JavaServer Faces 2.2 的视图操作特性为开发人员构建服务器端用户接口提供了几个重要优势。

2011 年 11 月发布

下载:

下载示例代码 (Zip)

简介

在本文中,我们来看看 JavaServer Faces 2.2 的一个可以使 JavaServer Faces 用户生活更轻松的新特性。

注意:JavaServer Faces 2.2 尚未正式发布。可以从这里获取有关下载和安装最新 JavaServer Faces 2.2 代码 (Mojarra 2.2.0-SNAPSHOT) 的说明。而且,还可以从这里下载本文所述代码的源文件。

上一版 (JavaServer Faces 2.0) 的两个新特性是书签功能和能够查看参数。这些特性为开发人员处理 GET 请求并将请求中传递的参数绑定到模型中的属性提供了一种机制。

例如,清单 1 是一个将目录项 id 作为输入的简单索引页面:

<!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">
    <f:metadata>
        <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    </f:metadata>
    <h:head>
        <title>Catalog Entry< /title>
    </h:head>
    <h:body>
        <h:form>
            Enter catalog number: 		
            <h:inputText id="item" value="#{catalog.item}"/>
            <h:commandButton value="submit" 
            action="catalog?faces-redirect=true&includeViewParams=true"/>
        </h:form>
    </h:body>
</html>

清单 1. 简单索引页面

该页面所指向的任何包含 <f:viewParam> 标记的页面可以从 GET 请求提取参数并提供给绑定属性,如清单 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">
    <h:head>
        <title>Catalog View</title>
    </h:head>
    <f:metadata>
        <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    </f:metadata>
    <h:body>
        You requested catalog item: <h:outputText value="#{catalog.item}"/>
    </h:body>
</html>

清单 2. <f:viewParam> 标记

假设在处理索引页面上输入的项目号之前,您想要检查项目 id 是否在某个范围内(或其他一些更复杂值的检查)。一种办法是在 inputText 组件内部向索引页面添加一个验证器:

Enter catalog number:
<h:inputText id="item" value="#{catalog.item}">
    <f:validator validatorId="com.jsf.sample.ValidItemRangeValidator"/>
</h:inputText>

当然,还需要创建一个类来实现 javax.faces.validator.Validator,并在 validate 方法中加入验证项目范围的逻辑。在清单 3 中,ValidItemRange 是一个定义上下限整数值范围的简单 bean。

@FacesValidator("com.jsf.sample.ValidItemRangeValidator")		
public class ValidItemRangeValidator implements Validator, Serializable {
    @Inject private ValidItemRange range;

    @Override
    public void validate(FacesContext fc,
                         UIComponent uic,
                         Object value) throws ValidatorException {
        Integer item = (Integer)value;
        if (item.intValue() >= range.getLow() && 
            item.intValue() <= range.getHigh()) {
            return;
        }
        ((UIInput) uic).setValid(false);
        fc.addMessage(null, 
            new FacesMessage("The item number you entered is invalid."));
    } 
}

清单 3. ValidItemRange Bean

在 JavaServer Faces 2.2 中,有一个更方便、更灵活的办法来执行此范围检查。JavaServer Faces 2.2 利用 Seam 3 中的 viewAction 组件这个特性进一步扩展了 GET 处理。

视图操作运行起来与按钮命令 (UICommand) 组件类似。默认情况下,它在调用应用程序阶段执行以响应初始请求。不过,正如我们将要看到的,可以在生命周期的任何阶段(可选择在回传阶段)调用视图操作,这使得视图操作非常适合执行预览检查。

使用 viewAction 组件

JavaServer Faces 2.2 定义了一个新标记 <f:viewAction>。此标记是一个 ActionSource2 UIComponent,它指定特定于应用程序的操作。viewAction 组件声明为元数据 facet (<f:metadata>) 的子组件。这就可以将视图操作并入到非 faces(初始)和 faces(回传)请求的 JavaServer Faces 生命周期内。

在我们的目录示例中,可以使用一个更简单的视图操作来替换验证器,如下所示:

<f:metadata>
    <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    <f:viewAction action="#{catalog.checkItem}"/>
</f:metadata>

:viewAction 组件必须声明为元数据 facet 的子组件。这就可以将视图操作作为非 faces 和 faces 请求的 JavaServer Faces 生命周期的一部分。如果将其放在页面中的任何其他位置,其行为将处于未定义状态。

注意,视图操作将调用 Catalog bean 的一个验证方法 (checkItem),这样就不再需要 Validator 类。此外,该方法返回一个可用于隐式导航的字符串,如清单 4 所示:

@Named
@RequestScoped
public class Catalog implements Serializable {

    private Integer item;
    private FacesContext facesContext;
    @Inject private ValidItemRange range; // An ApplicationScoped Bean
					            // the declares a high and low int
    @PostConstruct
    public void postConstruct() {
        facesContext = FacesContext.getCurrentInstance();
    }

    public String checkItem() {
        if (item.intValue() >= range.getLow() && 
            item.intValue() <= range.getHigh()) {
            return null;
        }
        facesContext.addMessage(null, 
            new FacesMessage("The item number you entered is invalid."));
        return "index";
    }

    // ... other getter and setter methods
}

清单 4. 隐式导航

正如其他 UICommand 组件一样,viewAction 组件也支持声明式导航。因此您可以编写在页面呈现之前查询的导航规则。如果规则匹配,则发生导航,就像这是回传一样。

<navigation-rule>
    <from-view-id>/index.xhtml</from-view-id>
    <navigation-case>
        <from-action>#{catalog.checkItem}</from-action>
        <if>#{catalog.invalidItem}</if>
        <to-view-id>/index.xhtml</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>

默认情况下,视图操作不在回传时执行,因为 viewAction 组件的设计是为了支持非 faces 请求。如果您的 viewAction 组件想要既能在非 faces 请求中执行也能在 faces 请求中执行,则可以启用回传请求的视图操作:

<f:viewAction action="#{catalog.checkItem}" onPostback="true"/>

默认情况下,在调用应用程序阶段执行视图操作。不过,JavaServer Faces 2.2 中的视图操作可以在 JavaServer Faces 生命周期的任何阶段中调用。您可以使用 javax.faces.event.PhaseID 类中定义的阶段常量的名称指定在哪个阶段执行操作。

<f:viewAction action="#{catalog.checkItem}" phase="UPDATE_MODEL_VALUES"/>

在前面的目录示例中,只有少数几个可用的阶段有意义,特别是更新模型值阶段之后那些阶段,因为应用程序将在此阶段验证写入的用户输入。

为了与现有的 <h:commandButton><h:commandLink> 标记保持一致,还可以在应用请求值阶段而不是默认阶段使用 immediate 属性调用视图操作:

<f:viewAction action="#{catalog.buildCustomForm}" immediate="true"/>

如果设置了任何阶段,它将优先于 immediate 属性。

:视图操作可以放在不含任何其他视图参数的视图元数据 facet 中。尽管 JavaServer Faces 规范要求视图元数据 facet 中至少有一个视图参数以便在初始请求中处理视图元数据 facet,但 JavaServer Faces 2.2 放宽了该项要求。

视图操作特性与 PreRenderViewEvent 的比较

PreRenderViewEvent 监听器是另一个用于在 JavaServer Faces 2.0 中在呈现页面之前执行评估的方法。我们的目录示例中,可以对 preRenderView 类型的事件使用监听器来检查项目号以进行验证:

<f:metadata>
    <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    <f:event type="preRenderView" listener="#{catalog.checkItem}"/>
</f:metadata>

不过,相对于在呈现页面之前执行评估的方法,JavaServer Faces 2.2 的视图操作特性提供了一些重要优势:

  • 视图操作可以在生成完整的组件树之前提前触发,从而产生更轻型的调用。

  • 视图操作在时间上更加可控。

  • 视图操作可用于与 GET 请求相同的上下文环境。

  • 视图操作同时支持隐式和显式导航。

  • 视图操作同时支持非 faces(初始)和 faces(回传)请求。

初期阶段操作的一个实际应用是上下文感知授权检查,例如,当应用程序用户尝试加载一个复杂页面,但该用户未被授权查看该页面(及其内容)时。使用 viewAction 组件,可以轻松评估(或确定)用户凭证并相应地进行导航。此检查可以提前进行,而不是在呈现响应阶段进行,从而防止发生其他(可能代价高昂的)副作用或阻止进程运行。

总结

总之,viewAction 组件简化了对初始请求和回调请求执行条件检查的过程,可以控制在生命周期的哪个阶段执行操作,既可以进行隐式导航又可以进行声明式导航。请试用!

另请参见

关于作者

Tom McGinn 是 Oracle 的 Oracle 服务器技术的主要课程设计人员。