跳至内容 Java Solaris 社区 Sun 商店 加入 SDN 我的个人档案 加入的益处
 
Java Studio Creator 2 应用程序模型
 
作者:David Botterill,编辑:Beth Stearns,2006 年 4 月 19 日  
Sun Java Studio Creator 2 集成开发环境 (Integrated Development Environment, IDE) 引用了一个概念,即“应用程序模型”,它描述了 Java Studio Creator 2 Web 应用程序的组成。本文介绍了该应用程序模型并说明了它如何与 Java Studio Creator 2 Web 应用程序的特定部分关联。该应用程序模型包含 IDE 设计时环境和应用程序运行时环境。在阅读本文的过程中,请务必牢记应用程序模型的这一完整范围。您可能已通过 IDE 早期版本熟悉了该应用程序模型。但是,与早期的 Java Studio Creator 2004Q2(版本 1)相比,Java Studio Creator 2 应用程序模型已进行了重大改进。本文介绍的是当前的应用程序模型,而不是对该模型进行的更改。

通过了解应用程序模型的概念,各个级别的软件开发者(从刚开始使用 Java Studio Creator 2 IDE 的开发者到高级开发者)都会从中受益。熟悉应用程序模型的开发者可以更好地了解 IDE 生成的 Web 应用程序的结构。要最大限度地从本文中获取信息,开发者应该具备 JavaTM 平台和 JavaServerTM Pages 的一般知识。由于该应用程序模型基于 JavaServer FacesTM 技术应用程序模型,因此熟悉 JavaServer Faces 技术也是很有帮助的。正因为如此,本文对了解应用程序模型所需的 JavaServer Faces 概念进行了介绍。本文:
  • 介绍了一种应用程序模型并指出了模型至关重要的原因。
  • 简要介绍了 JavaServer Faces 技术应用程序模型,它是 Java Studio Creator 2 应用程序模型的基础模型。
  • 详细介绍了 Java Studio Creator 2 IDE 应用程序模型的设计时环境和运行时环境。

什么是应用程序模型?
 

让我们先从术语“模型”的定义入手,因为它具有很多不同的含义和应用。在本文中,短语“模拟”是指模拟或抽象地表示系统。很多不同的学科使用模型来描述系统的不同部分。例如,在天气预报方面,天气模型表示已知的天气系统。在机械领域,可以使用不同类型的发动机模型,如点燃式发动机和喷气式发动机。

模型表示所需的基本元素,以及这些元素如何关联在一起以使系统正常工作。例如,点燃式发动机模型可能显示燃烧室、活塞、某种燃料以及火花塞。模型描述将解释这些不同的部分如何进行交互以使系统“点燃式发动机”正常工作。Web 站点 http://auto.howstuffworks.com/engine7.htm 显示了一个很好的示例。

应用程序模型是组成软件系统(称为“应用程序”)的各个部分的抽象表示。本文将重点介绍一种特定类型的应用程序,即“Web 应用程序”。

“客户端-服务器”应用程序模型是另一种类型的应用程序模型。在此模型中,服务器通过网络为客户端提供一些资源。客户端连接到服务器并使用提供的资源。图 1 显示了客户端-服务器应用程序的简图。数据库和电子邮件应用程序就是客户端-服务器应用程序的示例。
 

图 1:客户端-服务器应用程序
图 1:客户端-服务器应用程序
 
应用程序模型为开发者提供了正在开发的系统或应用程序的抽象或高级视图。了解应用程序模型有助于开发者创建 Web 应用程序,尤其是在应用程序设计、问题确定以及性能调节方面。在进行必要的设计决策时,您可以最大限度地使用应用程序模型。即使应用程序没有按预期方式进行工作,了解应用程序模型也有助于您确定所出现的问题。 

JavaServer Faces 技术应用程序模型
 

由于 Java Studio Creator 2 应用程序模型建立在 JavaServer Faces 技术应用程序模型的基础之上,因此我们将首先简要介绍 JavaServer Faces 技术应用程序模型的重要概念(有关 JavaServer Faces 技术的详细信息,请参见《J2EE 1.4 教程》的“JavaServer Faces 技术”一章)。

JavaServer Faces 技术基于基本的 Web 应用程序模型:客户端-服务器体系结构,其中客户端通常是浏览器,而服务器是应用程序或 Web 服务器。在此 Web 应用程序模型中,客户端调用内容请求,服务器给出由 HTML 或 XHTML 组成的响应。浏览器接收响应并以可读格式进行呈现。

JavaServer Faces 应用程序模型可以分解为三个更小的模型:

  • 用户界面模型
  • 导航模型
  • 支持 Bean 模型

本文将先介绍这些模型,然后再解释 JavaServer Faces 页面生命周期。

JavaServer Faces 用户界面模型
 

JavaServer Faces 用户界面模型包含组件 Java 类、呈现模型、事件和侦听程序模型、验证模型以及转换模型。

  • 组件 Java 类。JavaServer Faces 技术提供了一组 Java 类,它们表示通用用户界面组件,如按钮、输出字段和输入字段等。组件是以树状结构组织的,比较通用的组件放在树顶部,比较专用的组件放在通用组件的下面。

    用户界面模型中有两个重要的类:
    • UIViewRoot 组件类表示特定页面的所有组件的树根节点。
    • FacesContext 类用作每个请求信息和其他帮助程序类的访问点。

  • 呈现模型。JavaServer Faces 呈现(或用户界面)模型将组件定义与组件呈现分离开。通过按这种方式划分职责,可以使用不同的方法来呈现相同的组件定义。例如,在 Web 浏览器中可以将口令字段呈现为隐藏键入字符的输入字段。在 PDA 应用程序中呈现相同的组件时,可以在其输入字段中显示单词 "-assigned-" 以隐藏口令。

    呈现工具包定义了如何使用 JavaServer Pages 标记语言将组件映射到组件标记。JavaServer Faces 引用实现附带提供了一个标准 HTML 呈现工具包。


  • 事件和侦听程序模型。通过使用 JavaServer Faces 事件和侦听程序模型,开发者可以在用户界面 (User Interface, UI) 组件上注册侦听程序以处理事件,如按下按钮。此模型遵循基于 Observer 软件设计模式的标准 Java 基础类 (Java Foundation Classes, JFC)/Swing 模型(根据 Gang of Four 编写的《Design Patterns》一书,Observer 设计模式定义了对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并被自动更新)。这种设计模式允许将组件注册为生成事件的其他组件的观察者。在组件执行组成某个事件的操作时,它使用该事件通知所有注册的侦听程序(即观察者)。侦听组件负责对事件作出响应。JavaServer Faces 事件模型中有两种类型的事件:操作事件和值更改事件。
    • 操作事件:当用户执行某种操作(如按下按钮或单击超级链接)时,将引发操作事件。
    • 值更改事件:当用户更改组件的值(如单击复选框或在文本字段中输入值)时,将引发值更改事件。仅当组件没有验证错误时,才会引发值更改事件。

    JavaServer Faces 应用程序模型提供了两种指定事件处理程序的方法:

    1. 实现事件侦听程序类(如 ActionListenerValueChangeListener),然后将 actionListenervalueChangeListener 添加到组件的 JSP 标记中。
    2. 在支持 Bean 上定义一种方法(请参见支持 Bean 模型),并使用方法绑定表达式将该方法绑定到组件。
  • 转换模型。JavaServer Faces 转换模型允许在数据类型之间进行转换。将 JavaBean 服务器端对象(支持 Bean)作为转换器与 UI 组件关联时,JavaBean 对象控制数据的表示类型和模型类型。例如,JavaBean 对象可以选择将布尔型模型类型显示为值为 true 的字符串类型。


  • 验证模型。JavaServer Faces 验证模型提供了一种定义组件的定制验证的方法。例如,日期验证可确保输入的日期正确无误。


JavaServer Faces 导航模型
 

JavaServer Faces 导航模型定义了 Web 应用程序在页面之间移动的方式。导航模型包含一个处理程序,该处理程序使用一个包含导航规则的导航配置文件。处理程序响应操作源(如按钮或超级链接)执行的操作事件。

共有两种类型的导航:静态和动态。对于静态导航,在运行时之前使用导航配置文件和固定编码的操作方法返回值来确定导航规则。图 2 显示了一个导航配置文件的示例。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
    <navigation-rule>
        <from-view-id>/Page1.jsp</from-view-id>
        <navigation-case>
            <from-outcome>case1</from-outcome>
            <to-view-id>/Page2.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    <navigation-rule>
        <from-view-id>/Page2.jsp</from-view-id>
        <navigation-case>
            <from-outcome>back</from-outcome>
            <to-view-id>/Page1.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
</faces-config>
     

图 2:包含静态导航规则的导航配置文件

导航配置通过将 JavaServer Pages 操作方法返回值映射到 <to-view-id> 标记(用于标识要导航到的页面)来确定静态配置。图 3 显示了一个操作方法,它使 Web 应用程序按照图 2 所示的导航配置文件中的规则导航到 Page2.jsp。

public String button1_action() {

        return "case1";
    }
     

图 3:操作方法返回值

通过使用动态导航,开发者可以基于运行时条件来更改导航。开发者可以使用导航模型来定义一组从一个页面指向多个页面的导航规则。操作方法返回值确定了导航路线,程序逻辑可以推断出下一个页面。图 4 显示了一个包含多个 Page1.jsp 导航条件的导航配置文件。当同一页面具有多个导航条件时,允许在操作方法中以合乎逻辑的方式来确定导航。图 5 显示了一个由下拉列表值更改触发的方法。该操作方法动态地确定了要导航到的页面。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
    <navigation-rule>
        <from-view-id>/Page1.jsp</from-view-id>
        <navigation-case>
            <from-outcome>case1</from-outcome>
            <to-view-id>/Page2.jsp</to-view-id>
        </navigation-case>
        <navigation-case>
           <from-outcome>case2</from-outcome>
            <to-view-id>/Page3.jsp</to-view-id>
        </navigation-case>
        <navigation-case>
            <from-outcome>case3</from-outcome>
            <to-view-id>/Page4.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    <navigation-rule>
        <from-view-id>/Page2.jsp</from-view-id>
        <navigation-case>
            <from-outcome>back</from-outcome>
            <to-view-id>/Page1.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
</faces-config>
	 

图 4:包含动态导航规则的导航配置文件

      public void dropDown1_processValueChange(ValueChangeEvent vce) {
      if(dropDown1.getSelected().equals("one")) {
         return case1;
      } else if(dropDown1.getSelected().equals("two")) {
         return case2;
      } else if(dropDown1.getSelected().equals("three")) {
         return case3;
      } else return null;
}
	 

图 5:用于动态导航的值更改操作方法

JavaServer Faces 支持 Bean 模型
 

JavaServer Faces 应用程序模型将用户界面与表示数据模型的后端对象分离开。表示数据和状态的服务器端或后端对象称为支持 Bean。通常,JavaServer Faces 应用程序至少包含一个支持 Bean。
支持 Bean 是一种 JavaBean 组件,它可以包含:

  • 用户界面组件属性
  • 事件处理方法
  • 验证方法
  • 转换器初始化代码
您可以将用户界面组件的值绑定到支持 Bean 属性上,也可以将组件的实例绑定到支持 Bean 属性上。支持 Bean 通过使用 JavaServer Faces 表达式语言 (Expression Language, EL) 绑定到 JavaServer Pages 中的用户界面组件上。JavaServer Faces EL 以 #{} 语法表示。

值绑定

如果将组件的值绑定到支持 Bean 属性上,则会将组件的值连接到关联的支持 Bean 属性的 getter 和 setter 方法上。图 6 显示了支持 Bean 中将名为 firstName 的属性绑定到用户界面组件上的 Java 代码。


String firstName = null;

String getFirstName(){
  return firstName;
}

void setFirstName(String inFirstName){
   firstname = inFirstName;
}

图 6:用于绑定属性的支持 Bean 代码

图 7 显示了将用户界面组件绑定到支持 Bean 上所需的 JavaServer Faces EL。

<h:outputText value="#{MyBean.firstName}" />

图 7:用于将组件绑定到支持 Bean 上的 JavaServer Faces EL

组件实例绑定

如果支持 Bean 中的事件处理程序需要动态地更改有关组件的任何内容,则将组件实例绑定到支持 Bean 上是非常有用的。将组件实例绑定到支持 Bean 上时,将使用组件的 binding 属性来指定要与组件实例关联的支持 Bean 属性。例如,图 8 显示了将用户界面组件绑定到支持 Bean 上所需的 JavaServer Faces EL。

<h:outputText binding="#{MyBean.outputText1}" id="outputText1" 
value="Hello World" />

图 8:将组件绑定到支持 Bean 上

绑定后,支持 Bean 将包含组件实例 outputText1 的 getter 和 setter 方法。图 9 显示了支持 Bean 源代码的大致内容。

HtmlOutputText outputText1 = new HtmlOutputText();

HtmlOutputText getOutputText1(){
  if(showLabel) {
    outputText1.setRendered(true);
  } else {
    outputText1.setRendered(false);
  }
  return outputText1;
}

void setOutputText1(HtmlOutputText inOutputText){
   outputText1 = inOutputText;
}

图 9:具有实例绑定的支持 Bean 代码

请注意,支持 Bean 包含将组件呈现动态地设置为 true 或 false 的逻辑。实例绑定大大简化了使 UI 组件的动态更改在支持 Bean 中生效的过程。如果未使用实例绑定,每次需要进行更改时,必须编写代码以获取 UI 组件。图 10 显示了该代码的大致内容。

HTMLOutputText outputText1 = getFacesContext().getViewRoot()
.findComponent(":form:outputText1");

if(null != outputText1) {
  //dynamically change UI component
} else {
  //deal with UI component not found condition
}

图 10:不具有实例绑定的虚构支持 Bean 代码

受管支持 Bean

JavaServer Faces 技术提供了一个受管 Bean 工具来管理支持 Bean。受管 Bean 工具管理支持 Bean 的创建和持久性。要让 JavaServer Faces 将用户界面组件数据绑定到支持 Bean 上,必须在受管 Bean 工具配置文件中管理和声明支持 Bean。此外,您还必须提供包含起始标记 <managed-bean> 的 XML 配置文件,该标记指示您正在使用受管 Bean 工具。

图 11 显示了名为 managed-beans.xml 的受管 Bean 工具配置文件。当应用程序引用 Bean 时,该工具将在请求、会话或应用程序范围内创建、初始化和存储该 Bean。

<managed-bean>
<managed-bean-name>UserNumberBean</managed-bean-name>
<managed-bean-class>guessNumber.UserNumberBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>minimum</property-name>
<property-class>long</property-class>
<value>0</value>
</managed-property>
<managed-property>
<property-name>maximum</property-name>
<property-class>long</property-class><value>10</value>
</managed-property>
</managed-bean>

图 11:受管 Bean 工具配置文件

JavaServer Faces 页面生命周期
 

JavaServer Faces 页面遵循定义的生命周期。浏览器将显示 JavaServer Faces 页面,而最终用户操作(如按下按钮或单击超级链接)将生成 Servlet 请求。该请求遵循设置的逻辑路径,并最终生成且返回响应。在 JavaServer Faces 文档中,将处理请求并生成响应的过程称为生命周期。

有关 JavaServer Faces 生命周期的详细解释,请参见《J2EE 1.4 教程》的 JavaServer Faces 页面的生命周期一章。共有四种基本类型的请求/响应事件。

  1. JavaServer Faces 请求 - 从以前生成的 JavaServer Faces 响应发送的 Servlet 请求。
  2. 非 JavaServer Faces 请求 - 发送到服务器端组件(如 Servlet 或 JSP 页)的 Servlet 请求。非 JavaServer Faces 请求通过发出 JavaServer Faces JSP 页请求来启动 JavaServer Faces 应用程序,这又会生成 JavaServer Faces 响应。
  3. JavaServer Faces 响应 - 通过调用 JavaServer Faces 生命周期的呈现响应阶段来生成的 Servlet 响应。
  4. 非 JavaServer Faces 响应 - 从服务器端组件(如 Servlet 或 JSP 页)生成的 Servlet 响应,此类组件不包含 JavaServer Faces 组件。
处理 JavaServer Faces 请求并生成 JavaServer Faces 响应称为 JavaServer Faces 的标准请求处理生命周期。图 12 显示了此生命周期。

 
图 12:JavaServer Faces 标准请求处理生命周期
图 12:JavaServer Faces 标准请求处理生命周期


Java Studio Creator 2 应用程序模型
 

Java Studio Creator 2 应用程序模型基于 JavaServer Faces 应用程序模型。Java Studio Creator 开发小组大大改进了该应用程序模型,以使其更易于开发者理解并可以在 IDE 中使用。

本节介绍了部分特定于 Java Studio Creator 2 的应用程序模型,尤其阐述了支持 Bean 和用户界面组件模型之间的区别和对它们的特殊处理。除非另有说明,否则本节中的术语“应用程序模型”指的是 Java Studio Creator 2 应用程序模型,而不是 JavaServer Faces 应用程序模型。


用户界面支持 Bean
 

让我们先了解一下支持 Bean。应用程序模型包含一个用于处理用户界面详细信息的支持 Bean 类别。此支持 Bean 类别(称为“用户界面支持 Bean”)用于处理用户界面属性和事件。此类别中有两个支持 Bean:页面 Bean页面片段 Bean


页面 Bean
 

页面 Bean 表示一个 JavaServer Pages 页面。页面 Bean 包含用于管理一个 Web 页面在服务器端的所有实现逻辑。它不包含验证和转换逻辑。

用户界面组件绑定到服务器上的支持 Bean。JavaServer Faces 规范不限制 JavaServer Pages 页面和支持 Bean 之间的对应关系。例如,一个 Web 应用程序中可以有五个 JSP 页均绑定到同一支持 Bean 上。

但是,Java Studio Creator 应用程序模型有所不同。该应用程序模型指定了 JavaServer Pages 页面和页面 Bean 支持 Bean 之间的一对一关系。也就是说,对于每个 JSP 页,均有一个绑定了 JavaServer Pages 组件的页面 Bean。例如,IDE 中的可视设计器窗口就是 JSP 页的可视视图。图 13 描述了 IDE 的可视设计器、JSP 页和页面 Bean 之间的关系。  

图 13:JSP 页和页面 Bean 之间的关系
图 13:JSP 页和页面 Bean 之间的关系

应用程序模型还支持未关联 JSP 页的支持 Bean。“数据支持 Bean”一节讨论了这些类型的支持 Bean。

让我们深入剖析一下页面 Bean。页面 Bean 包含管理 Web 页的服务器端逻辑所需的所有内容。此逻辑可以分为两大类:UI 组件属性和事件。在 Java Studio Creator 应用程序模型中,超出页面 Bean 范围的独立类用于处理验证和转换。

页面 Bean UI 组件属性
 

让我们先了解一下支持 Bean。应用程序模型包含一个用于处理用户界面详细信息的支持 Bean 类别。此支持 Bean 类别(称为“用户界面支持 Bean”)用于处理用户界面属性和事件。此类别中有两个支持 Bean:页面 Bean页面片段 Bean

由于 JSP 页和页面 Bean 之间具有一对一的关系,因此,每次将 UI 组件添加到 IDE 可视设计器时,将在 UI 组件的关联页面 Bean 中添加一个属性。由于应用程序模型使用值绑定和组件实例绑定,因此,IDE 通过为组件实例绑定创建 getter 和 setter 并指定缺省值绑定来实现应用程序模型。图 14 显示了将基本静态文本组件放到 Page1 的可视设计器视图上时 IDE 创建的 JavaServer Pages 脚本。请注意对 ui:statictext 组件创建的 #{Page1.staticText1} 绑定。

<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="1.2" xmlns:f="http://java.sun.com/jsf/core" 
xmlns:h="http://java.sun.com/jsf/html" 
 xmlns:jsp="http://java.sun.com/JSP/Page" 
 xmlns:ui="http://www.sun.com/web/ui">
    <jsp:directive.page contentType="text/html;charset=UTF-8" 
	pageEncoding="UTF-8"/>
    <f:view>
        <ui:page binding="#{Page1.page1}" id="page1">
            <ui:html binding="#{Page1.html1}" id="html1">
                <ui:head binding="#{Page1.head1}" id="head1">
                    <ui:link binding="#{Page1.link1}" id="link1" 
					url="/resources/stylesheet.css"/>
                </ui:head>
                <ui:body binding="#{Page1.body1}" id="body1"" 
				style="-rave-layout: grid">
                    <ui:form binding="#{Page1.form1}" id="form1">
                        <ui:staticText binding="#{Page1.staticText1}" 
						id="staticText1" style="position: 
                         absolute; left: 96px; top: 24px"/>
                    </ui:form>
                </ui:body>
            </ui:html>
        </ui:page>
    </f:view>
</jsp:root>
	 

图 14:Page1 绑定

图 15 显示了在页面 Bean 中为 ui:statictext 组件创建的 setter 和 getter。请注意,由于绑定类型为组件实例 (binding=),因此,返回值和传递给 setter 的值都是组件类型,而不是 Java 基元类型。这意味着,您可以在支持 Bean 中对 UI 组件执行一些操作(如 setRendered(false))以隐藏静态文本字段。

    private StaticText staticText1 = new StaticText();

    public StaticText getStaticText1() {
        return staticText1;
    }

    public void setStaticText1(StaticText st) {
        this.staticText1 = st;
    }
	 

图 15:页面 Bean 中的 getter 和 setter

事件

Java Studio Creator 2 应用程序模型支持 JavaServer Faces 操作和值更改事件。允许更改值的 Java Studio Creator 2 组件(如下拉列表或文本框)支持值更改事件。引发操作的组件(超级链接或按钮)支持操作事件。

IDE 提供了几种定义值更改事件或操作事件的方法。定义事件处理的最简便方法是双击组件。可视设计器将在 JSP 页和页面 Bean 中创建所需的条目以支持事件处理。

图 16 显示了为按钮创建的 JSP 页代码。

<ui:button action="#{Page1.button1_action}" binding="#{Page1.button1}" 
id="button1" style="position: absolute; left: 120px; top: 360px" 
text="go"/>
	 

图 16:按钮的 JSP 代码

图 17 显示了为支持操作事件而创建的页面 Bean 方法。

    public String button1_action() {
        // TODO: Process the button click action. Return value is a navigation
        // case name where null will return to the same page.        
        return null;
    }
	 

图 17:操作事件的页面 Bean 方法

请注意,图 17 中的方法名称与图 16 中的方法绑定表达式方法名称相匹配。

值更改事件的处理方式与操作事件稍有不同,唯一的原因是 JavaServer Pages 属性名称不同。请注意,图 18 中的值更改事件使用的属性是 valueChangeListener。

<ui:textField binding="#{Page1.textField1}" id="textField1" 
style="position: absolute; left: 120px; top:
144px" valueChangeListener="#{Page1.textField1_processValueChange}"/>
	 

图 18:值更改事件属性

图 19 显示了页面 Bean 中对应的值更改事件方法。在 JavaServer Faces 生命周期的调用应用程序阶段,将在页面 Bean 上调用这些事件方法。下一节讨论了页面 Bean 生命周期。

    public void textField1_processValueChange(ValueChangeEvent event) {
        // TODO: Replace with your code
        
    }
	

图 19:值更改事件页面 Bean 方法

页面 Bean 生命周期
 

应用程序模型使用 JavaServer Faces 标准请求处理生命周期作为页面 Bean 生命周期。但是,Java Studio Creator 2 应用程序模型通过利用 JavaServer Faces 体系结构中称为阶段侦听程序的部分简化了该生命周期。通过利用阶段侦听程序,应用程序可以在图 12 所示的 JavaServer Faces 标准请求处理生命周期中插入逻辑。

应用程序模型定义了此请求处理生命周期中的以下插入点。

  • init
  • preprocess
  • prerender
  • destroy
这些插入点可用作页面 Bean 上的方法。通过使用这些方法,IDE 开发者可以利用 JavaServer Faces 标准请求处理生命周期来更好地控制 Web 应用程序逻辑。图 20 显示了非 JavaServer Faces 请求生成 JavaServer Faces 响应时覆盖 JavaServer Faces 生命周期的 Java Studio Creator 2 应用程序模型生命周期。
图 20:Java Studio Creator 2 应用程序模型生命周期示例 1
图 20:Java Studio Creator 2 应用程序模型生命周期示例 1

图 21 显示了 JavaServer Faces 请求生成 JavaServer Faces 响应时覆盖标准请求处理生命周期的 Java Studio Creator 2 应用程序模型生命周期。在图 20 和图 21 中,黄色框表示应用程序模型插入的生命周期方法。

图 21:Java Studio Creator 2 应用程序模型生命周期示例 2
图 21:Java Studio Creator 2 应用程序模型生命周期示例 2

在页面 Bean 构造函数完成后,将在恢复视图阶段结尾调用 init 方法。该 init 方法用于获取页面 Bean 随后在 preprocessprerender 方法中使用的资源。例如,在 init 方法内,您可以建立数据库连接并获取休眠会话等。

在恢复组件树之后(但在进行任何事件处理之前),将在应用请求值阶段开始时调用 preprocess 方法。您可以将逻辑放在 preprocess 方法中以设置 UI 组件的 required 属性。通过使用 required 属性,开发者可以动态地为给定 UI 组件打开或关闭验证。图 22 是一个此类代码的示例。

public void preprocess() {
   FacesContext context = FacesContext.getCurrentInstance();
   Map map = context.getExternalContext().getRequestParameterMap();
   // see if the "next" button was sent in the request which would tell us the 
   // "next" button was pressed.
   if (map.containsKey(nextButton.getClientId(context))) {
      shippingMethodDropDown.setRequired(true);
   } else {
      shippingMethodDropDown.setRequired(false);
   }
}

图 22:preprocess 方法

在发生实际页面呈现之前,将在呈现响应阶段开始时为要呈现的页面调用 prerender 方法。prerender 方法非常适于放置需要在呈现页面之前执行的逻辑。例如,您可以在此方法中包含逻辑,以确保将绑定的数据提供器放在表内的正确行中。

destroy 方法(在呈现响应阶段结尾调用)释放为处理页面而获取的资源。此方法非常适于清理在其他生命周期方法期间分配的资源。

利用请求处理生命周期
 

要利用 Java Studio Creator 2 应用程序模型生命周期,您应该了解每个阶段在 JavaServer Faces 标准请求处理生命周期中执行的任务。本节简要介绍了标准生命周期中的每个阶段。有关更详细的解释,请参阅 JavaServer Faces 规范

标准请求处理生命周期阶段如下所示:

  • 恢复视图阶段 - 在恢复视图阶段将执行以下任务:
    • 如果 FacesContext 中存在 UIViewRoot:
      • 在 UIViewRoot 上设置语言环境。
      • 对于 UIViewRoot 中的每个组件,如果使用组件实例绑定,则在每个绑定上调用 setValue 方法。
      • 结束此阶段的处理。
    • 如果此 FacesContext 中不存在任何 UIViewRoot,则此阶段将派生一个视图标识符。如果它无法确定视图标识符,则会抛出异常并结束此阶段的处理。
    • 使用派生的视图标识符调用 viewHandler.restoreView 以获取 UIViewRoot。如果找不到任何 UIViewRoot,则将创建一个 UIViewRoot。
    • 恢复或创建的 UIViewRoot 将被存储在 FacesContext 中。
    • 对于 UIViewRoot 中的每个组件,如果使用组件实例绑定,则将在每个绑定上调用 setValue 方法。
  • 应用请求值阶段 - 在应用请求值阶段将执行以下操作:
    • 对于实现 ActionSource 的组件(如按钮和超级链接),在激活它们时创建 ActionEvent。当用户单击按钮或选择超级链接时,将激活这些组件。如果将组件的 immediate 属性设置为 true,则会在应用请求值阶段结尾传递 ActionEvent。否则,会在调用应用程序阶段结尾传递 ActionEvent。
    • 对实现 EditableValueHolder 的组件(如文本字段)执行验证和转换(如果也将这些组件的 immediate 属性设置为 true)。
    • 在此阶段结尾,通过请求中提交的值来更新所有 EditableValueHolder 组件。
  • 进程验证阶段 - 在进程验证阶段将执行以下操作:
    • 执行所有组件验证,除非将组件的 immediate 属性设置为 true(在这种情况下,已经在应用请求值阶段进行了验证)。
    • 执行所有组件转换。
    • 如果验证或转换失败,则将相应的事件放在队列中。在该阶段结束并且处理了事件之后,将控制权直接传递给呈现响应阶段,该阶段将处理这些错误。
    • 如果在 validate 方法中调用 responseComplete,则会终止生命周期处理。
    • 如果在 validate 方法中调用 renderResponse,则将控制权传递给呈现响应阶段。
  • 更新模型值阶段 - 在更新模型值阶段将执行以下操作:
    • 通过在所有组件上调用 updateModel 方法来更新应用程序的模型数据。此方法调用将使用每个组件的本地数据来更新模型。
    • 清除组件的本地数据。
    • 如果在 updateModel 方法中调用 responseComplete 方法,则会终止生命周期处理。
    • 如果在 updateModel 方法中调用 renderResponse 方法,则将控制权传递给呈现响应阶段。
  • 调用应用程序阶段 - 在调用应用程序阶段将执行以下操作:
    • 广播任何已排队的事件。对于 Java Studio Creator 2 应用程序模型,此阶段将调用操作方法,如按钮操作。
    • 此阶段还会与 JavaServer Faces 页面导航工具进行交互,以检查操作方法返回值并且还可能导航到不同的页面。如果导航到其他页面,则遵循非 JavaServer Faces 页面到 JavaServer Faces 页面的请求类型方案,如图 20 所示。
  • 呈现响应阶段 - 在呈现响应阶段执行的操作如下所示:
    • 向客户端呈现响应。
    • 为后续请求保存响应状态。


页面片段 Bean
 

除了插入的方法外,页面片段 Bean 和页面 Bean 是相同的。页面片段 Bean 只包含 initdestroy 生命周期方法。请参阅图 21 以了解这两个插入方法是如何覆盖 JavaServer Faces 标准请求处理生命周期的。

Portlet 生命周期
 

在 Java Studio Creator 2 应用程序模型中,Portlet 页面和 Web 应用程序页面除了页面生命周期存在差异外都是相同的。要充分了解这些生命周期差异,您应该先了解门户如何与 Portlet 进行交互。JSR-168 Portlet 规范定义了门户与 Portlet 之间的交互。

通常,Web 浏览器中显示的门户包含多个 Portlet。也就是说,当浏览器显示门户页面时,实际有多个 Portlet 显示或呈现其内容。门户将管理显示的 Portlet。在内部,门户负责完成以下两个任务:

  1. 它通知 Portlet 何时显示其自身。
  2. 它通知 Portlet 是否在该 Portlet 内部执行了某个操作,如按下按钮。
当门户希望其 Portlet 显示其内容时,门户会向其中显示的每个 Portlet 发送呈现请求。如果门户中显示的 Portlet 包含绑定到操作请求的按钮,当按下该按钮时,门户会将该 Portlet 的操作请求放在队列中。此外,门户还为该 Portlet 以及页面上的所有其他 Portlet 生成呈现请求。您应该注意到,门户将其中显示的 Portlet 上的每个操作方法的操作和呈现请求都放在队列中。

这种交互意味着,呈现的 Portlet 页面无法对要显示的值的状态作出假设。与 Web 应用程序页面不同,Portlet 页面不能假设要在呈现响应阶段呈现的页面就是在恢复视图阶段生成的同一页面。如果 Portlet 要在重复呈现请求之间保持状态,则它必须使用会话 Bean 来存储状态信息。

图 23 显示了调用操作(传回)并且要呈现同一 Portlet 页面时 Portlet 页面的生命周期。请注意,创建了两个不同的 Page1 实例。还请注意,Page1 的实例 1 从不会执行呈现响应阶段,这意味着从不会为 UIViewRoot 保存组件值。因此,当创建了 Page1 的实例 2 并且该实例执行恢复视图阶段时,UIViewRoot 指向的组件树不会在 Page1 的实例 1 中输入值。Portlet 页面必须始终做好准备以便从头开始或通过会话数据呈现其值,切记这一点至关重要。这意味着,您决不能将 Portlet 页面的 UI 组件绑定到页面 Bean 属性或请求 Bean 属性上。此外,也决不能依赖于可能在操作事件过程中设置的页面 Bean 实例变量。

图 23:Portlet 页面生命周期
图 23:Portlet 页面生命周期

让我们看一下 Portlet 生命周期的图表以了解它是如何影响页面呈现的。假定页面 Bean 包含一个静态文本字段和一个按钮,如图 24 所示。

图 24:包含按钮和静态文本字段的页面 Bean
图 24:包含按钮和静态文本字段的页面 Bean

此外,还假定您在页面 Bean 上定义了一个名为 myString 的属性,并将静态文本字段绑定到该属性上。如果按钮操作方法包含图 25 中所示的代码,在运行 Portlet 应用程序并按下该按钮时,静态文本字段将为空白。

public String button1_action() { 
    this.myString = "Hello World";
    return null;
}

图 25:值更改事件页面 Bean 方法

通常,您希望静态文本字段包含短语 Hello World,而此处并不是这种情况。按下按钮时,按钮操作方法将设置 myString 属性,该属性位于 PageBean1 的实例 1 上。当 Portlet 容器向 Portlet 发送呈现请求时,这会创建 PageBean1 的实例 2,并且实例 2 上的 myString 属性是空白的。

数据支持 Bean
 

数据支持 Bean 与用户界面支持 Bean 不同。数据支持 Bean 用于管理数据,它与任何 JSP 页都没有关联。请求 Bean、会话 Bean 以及应用程序 Bean 都是数据支持 Bean。

数据支持 Bean 范围和生命周期

由于数据支持 Bean 与 JSP 页没有关联,因此,数据支持 Bean 的生命周期与页面 Bean 不同。由于这些支持 Bean 用于管理数据,因此,您需要了解它们的范围。Java Studio Creator 2 应用程序模型为数据支持 Bean 定义了三个范围:应用程序范围、会话范围和请求范围。

  • 应用程序范围 - 应用程序范围中的对象是在应用程序级别创建的,并且可供所有用户、会话和请求使用。使用应用程序范围的一个很好的示例是,使用下拉列表来保存需要由所有用户共享的数据。
  • 会话范围 - 会话范围中的对象是在用户会话级别创建的。它们可供会话以及会话中的所有请求使用。会话范围用于共享数据资源,如行集和数据提供器。
  • 请求范围 - 请求范围中的对象是为每个请求创建的,并且只能在请求过程中使用。请求范围用于在两个页面之间传递信息。例如,您可能将一组 Web 页用作向导来收集有关订单的信息。每次按下继续按钮时,就会将当前页面的值存储在请求范围内,并导航到向导中的下一页。

所有数据支持 Bean 都是由 JavaServer Faces 受管 Bean 工具管理的。该工具仅当需要时才会创建数据支持 Bean,这种方法称为“延迟实例化”。以下介绍了这种延迟实例化的工作方式。

在恢复视图生命周期阶段调用 setValue 方法时,JavaServer Faces 受管 Bean 工具将创建一个数据支持 Bean(如果尚未存在)。如果该数据支持 Bean 具有一个与 UI 组件绑定的属性,则会调用 setValue 方法。新创建的数据支持 Bean 将放在相应的范围内。如果从另一个支持 Bean 引用该支持 Bean,受管 Bean 工具还会创建一个数据支持 Bean,并将其放在相应的范围内。

在使用这种延迟实例化时,使用应用程序模型的开发者不应该依赖于数据支持 Bean 的实例化。即使创建了特定范围,这也并不意味着同样创建了与该范围关联的支持 Bean。因此,应该避免将任何所需的实例化代码放在数据支持 Bean 构造函数中,而是将所有实例化代码放在支持 Bean 的 init 方法中。

应用程序 Bean

应用程序 Bean 是在应用程序范围内创建的,用于在应用程序级别管理数据。应用程序 Bean 包含两个生命周期方法:initdestroy

将应用程序 Bean 作为属性添加到 Servlet 上下文时,将调用 init 方法。在值绑定表达式中计算应用程序 Bean 名称的值时,会将应用程序 Bean 作为属性添加到 Servlet 上下文中。

应用程序 Bean 作为属性从 Servlet 上下文中删除时,将调用 destroy 方法。当 JavaServer Faces 受管 Bean 工具确定受管 Bean 范围是应用程序时,它将控制在 Servlet 上下文中添加和删除应用程序 Bean 的操作。受管 Bean 工具可能会由于以下原因而删除应用程序 Bean:

  • 应用程序逻辑明确地删除应用程序 Bean。
  • 从应用程序或 Web 容器中取消部署应用程序。

会话 Bean

会话 Bean 是在会话范围内创建的,用于在会话级别管理数据。会话 Bean 包含四个生命周期方法:initpassivateactivatedestroy

将会话 Bean 作为属性添加到会话时(在值绑定表达式中计算会话 Bean 名称的值时将发生这种情况),将调用 init 方法。

会话处于钝化状态时(即暂时将其状态保存到数据存储中),将调用 passivate 方法。Servlet 容器通常钝化和激活会话,以使其能够将会话传递给远程 Servlet 容器。将会话传递给远程容器意味着,会话 Bean 上的每个类的属性都可以被序列化或标记为 transientpassivate 方法用于对类属性执行任何特殊序列化。在 passivate 方法中处理的类属性还需要在 activate 方法中进行重新创建。此外,如果类属性需要标记为 transient,该类属性可能是在 activate 方法中进行重新创建的很好候选对象。

在激活会话时将调用 activate 方法。此方法用于重新组合无法按标准方法序列化的信息。

将会话 Bean 作为属性从会话中删除时,将调用 destroy 方法。当 JavaServer Faces 受管 Bean 工具确定受管 Bean 范围是会话时,它将控制在会话中添加和删除会话 Bean 的操作。它可能会由于以下原因而删除会话 Bean:

  • 应用程序逻辑明确地删除会话 Bean。
  • 会话失效,如在用户注销时。
  • 会话超时。

请求 Bean

请求 Bean 是在请求范围内创建的,它包含两个生命周期方法:initdestroy

如果将 UI 组件属性绑定到请求 Bean 上,则会从 JavaServer Faces 标准请求处理生命周期的恢复视图阶段调用 init 方法。

在 JavaServer Faces 标准请求处理生命周期的呈现响应阶段结尾,将调用 destroy 方法。

 
用户界面组件
 

Java Studio Creator 2 应用程序模型包含四组用户界面组件:基本组件、JavaServer Faces 标准组件、定制组件以及非可视组件。

  • 基本组件 - 基本组件(基于 JavaServer Faces 组件)使用 Java Studio Creator 2 设计时应用程序编程接口 (Application Programming Interface, API)
  • 由于它们实现了此设计时 API,因此,IDE 可以提供比 JavaServer Faces 标准组件更丰富的可视设计环境(Java Studio Creator 2 设计时 API 已经提交 JCP 以对其进行标准化,即作为 JavaBeansTM JBDT 的 JSR 273 设计时 API。其中,JCP 是指 Java Community Process - Java 社区组织;JSR 是指 Java Specification Request - Java 规范请求)。基本组件是一组综合的组件,它包括但不限于以下组件:
    • 静态文本
    • 文本字段
    • 按钮
    • 带有弹出式日期选取器的日历
    • 标签集
  • JavaServer Faces 标准组件 - JavaServer Faces 标准组件是 JavaServer Faces 引用实现附带提供的组件。如果您熟悉早期的 Java Studio Creator 2004Q2 IDE,这些组件就是以前的标准组件。
  • 定制组件 - 由于 IDE 组件模型是可扩展的,因此,它提供了用于添加定制组件的基础结构。组件模型指定了定义每个组件的运行时和设计时行为的方法,以便第三方组件供应商能够在 Java Studio Creator 2 应用程序模型中插入定制组件。
  • 非可视组件 - 您可以使用应用程序模型来定义和使用非可视组件。转换器、验证器和数据提供器都是非可视组件的示例。
 

将应用程序模型应用于 Web 应用程序

 了解 Java Studio Creator 2 应用程序模型的最佳方式是查看工作示例。样例应用程序 Corporate Travel Center 可以帮助您了解应用程序模型。下载样例。将文件解压缩到 CorporateTravelCenter 目录中,并在 Java Studio Creator 2 IDE 中打开此项目。在 IDE 中打开此项目后,查看“项目”窗口。请参见图 26。

图 26:Corporate Travel Center 的“项目”窗口
图 26:Corporate Travel Center 的“项目”窗口

请注意显示在“Web 页”节点下面的具有 .jsp 文件扩展名的文件。这些文件是用户界面的 JSP 页。双击 Page1.jsp,以在可视设计器中打开 Page1。打开此文件后,您应该会在可视设计器编辑工具栏中看到三个按钮(“设计”、"JSP" 和 "Java")。请参见图 27。单击 "Java" 按钮,将打开 Java 编辑器,并显示 Page1 页面 Bean 的源代码。请记住,在 Java Studio Creator 2 Web 应用程序中,每个 JavaServer Pages 页都有一个页面 Bean。

图 27:可视设计器编辑工具栏
图 27:可视设计器编辑工具栏
现在,让我们看一下页面生命周期方法。在 Java 编辑器中打开 Page1 页面 Bean 时,可以看到该页面 Bean 的四个生命周期方法:
  • init
  • preprocess
  • prerender
  • destroy

init 方法包含图 28 所示的代码。

/** 
     * <p>Callback method that is called whenever a page is navigated to,
     * either directly via a URL, or indirectly via page navigation.
     * Customize this method to acquire resources that will be needed
     * for event handlers and life cycle methods, whether or not this
     * page is performing post back processing.</p>
     * 
     * <p>Note that, if the current request is a postback, the property
     * values of the components do not represent any
     * values submitted with this request.  Instead, they represent the
     * property values that were saved for this view when it was rendered.</p>
     */
    public void init() {
        // Perform initializations inherited from our superclass
        super.init();
        // Perform application initialization that must complete
        // *before* managed components are initialized
        // TODO - add your own initialization code here

        // <editor-fold defaultstate="collapsed" desc="Creator-managed 
		Component Initialization">
        // Initialize automatically managed components
        // *Note* - this logic should NOT be modified
        try {
            _init();
        } catch (Exception e) {
            log("Page1 Initialization Failure", e);
            throw e instanceof FacesException ? (FacesException) e: new 
			FacesException(e);
        }

        // 
        // Perform application initialization that must complete
        // *after* managed components are initialized
        // TODO - add your own initialization code here
    }
  

图 28:Page1 init 方法代码

请注意,有两个注释(下面以粗体显示)用于通知开发者将代码放在受管组件部分之前或之后。将为页面 Bean 调用一次 init 方法中的代码。init 方法内的逻辑应该为页面初始化资源。

此样例中的 preprocess 方法不包含任何专门的代码。

prerender 方法包含图 29 所示的代码。

/** 
     *<p>Callback method that is called just before rendering takes place.
     * This method will <strong>only</strong> be called for the page that
     * will actually be rendered (and not, for example, on a page that
     * handled a postback and then navigated to a different page). Customize
     * this method to allocate resources that will be required for rendering
     * this page.</p>
     */
    public void prerender() {
        if (getSessionBean1().getPersonId() == null ) {
         try {
           personDataProvider.cursorFirst();
           getSessionBean1().getTripRowSet().setObject(1, 
		   personDataProvider.getValue("PERSON.PERSONID"));
           getSessionBean1().setPersonId((Integer)personDataProvider.getValue
		   ("PERSON.PERSONID"));
           tripDataProvider.refresh();
         } catch (Exception e) {
           error("Cannot switch to person " + 
		   personDataProvider.getValue("PERSON.PERSONID"));
           log("Cannot switch to person " + 
		   personDataProvider.getValue("PERSON.PERSONID"), e);
          }
       }else {
           dropDown1.setSelected(getSessionBean1().getPersonId());
        }
   }
  

图 29:Page1 prerender 方法

prerender 方法中的代码说明了前面讨论的一些概念。请注意 getSessionBean1 方法调用,它用于获取和设置具有会话范围的会话 Bean 数据。另请注意对下拉列表 (dropDown1) 组件实例绑定的引用。dropdown1.setSelected(getSessionBean1().getPersonId()); 语句动态地更改下拉列表组件的 selected 值。

destroy 方法包含图 30 所示的代码。

    /** 
     * <p>Callback method that is called after rendering is completed for
     * this request, if <code>init()</code> was called (regardless of whether
     * or not this was the page that was actually rendered). Customize this
     * method to release resources acquired in the <code>init()</code>,
     * <code>preprocess()</code>, or <code>prerender()</code> methods (or
     * acquired during execution of an event handler).</p>
     */
    public void destroy() {
        tripDataProvider.close();
        personDataProvider.close();
    }
  

图 30:Page1 destroy 方法

请注意,destroy 方法处理或关闭页面 Bean 中使用的资源。确切地说,此样例代码关闭两个数据提供器以便正确地进行清理。

最后,让我们看一下此示例中的值绑定。单击可视设计器编辑工具栏中的 "JSP" 按钮(如图 27 所示),以打开 Page1.jsp 的 JavaServer Pages 脚本。尤其是,请注意下拉列表 ui:dropDown 的绑定定义。请参见图 31。

<ui:dropDown binding="#{Page1.dropDown1}"
converter="#{Page1.dropDown1Converter}" id="dropDown1" 
items="#{Page1.personDataProvider.options['PERSON.PERSONID,PERSON.NAME']}"
onChange="common_timeoutSubmitForm(this.form, 'dropDown1');" 
style="left: 144px; top: 216px; position: absolute; width: 144px" 
valueChangeListener="#{Page1.dropDown1_processValueChange}"/>
  

图 31:Page1 JavaServer Pages 脚本

由于 binding="#{Page1.dropDown1} 子句而使此定义支持实例绑定。UI 组件绑定到 Page1 支持 Bean(Java Studio Creator 2 中的一种页面 Bean)和 dropDown1Converter 属性。JavaServer Faces 绑定使用页面 Bean 中的转换器代码(如图 32 所示)。

    private IntegerConverter dropDown1Converter = new IntegerConverter();

    public IntegerConverter getDropDown1Converter() {
        return dropDown1Converter;
    }

    public void setDropDown1Converter(IntegerConverter ic) {
        this.dropDown1Converter = ic;
    }
	

图 32:Page1 转换器方法

 
小结

此时,您应该已很好地了解了 Java Studio Creator 2 IDE 应用程序模型及其从 JavaServer Faces 应用程序模型衍生而来的过程。此外,您还应该了解了应用程序模型生命周期及其方法,以及 Web 应用程序页面和 Portlet 应用程序页面之间的生命周期差异。

要了解 JavaServer Faces 的详细信息,您应该参阅以下文档:
 
更多的开发者资源

有关为开发者提供的更多技术提示、文章和专家建议,请访问 Sun Developer Network (SDN) 上的 Java Studio Creator 开发者资源。





David Botterill 在过去的 22 年里一直从事软件开发,他积极地参与了 Java 平台(自 1.0 版以来)的开发工作。作为 Java Studio Creator Portlet 开发功能的开发组负责人,他通过他的博客从 Portlet 开发者获得反馈以改进功能。David 还专门从事 IDE 的 Web 服务使用功能的开发工作。
 
Beth Stearns 撰写了许多有关 Java 技术的文章和书籍。最近,她与别人合著了一本有关 J2EE BluePrints 的书籍《Designing Web Services with the J2EE 1.4 Platform》(使用 J2EE 1.4 平台设计 Web 服务)。