什么是应用程序模型?
让我们先从术语“模型”的定义入手,因为它具有很多不同的含义和应用。在本文中,短语“模拟”是指模拟或抽象地表示系统。很多不同的学科使用模型来描述系统的不同部分。例如,在天气预报方面,天气模型表示已知的天气系统。在机械领域,可以使用不同类型的发动机模型,如点燃式发动机和喷气式发动机。
应用程序模型为开发者提供了正在开发的系统或应用程序的抽象或高级视图。了解应用程序模型有助于开发者创建 Web 应用程序,尤其是在应用程序设计、问题确定以及性能调节方面。在进行必要的设计决策时,您可以最大限度地使用应用程序模型。即使应用程序没有按预期方式进行工作,了解应用程序模型也有助于您确定所出现的问题。 JavaServer Faces 技术应用程序模型
由于 Java Studio Creator 2 应用程序模型建立在 JavaServer Faces 技术应用程序模型的基础之上,因此我们将首先简要介绍 JavaServer Faces 技术应用程序模型的重要概念(有关 JavaServer Faces 技术的详细信息,请参见《J2EE 1.4 教程》的“JavaServer Faces 技术”一章)。
本文将先介绍这些模型,然后再解释 JavaServer Faces 页面生命周期。 JavaServer Faces 用户界面模型
JavaServer Faces 用户界面模型包含组件 Java 类、呈现模型、事件和侦听程序模型、验证模型以及转换模型。
JavaServer Faces 应用程序模型提供了两种指定事件处理程序的方法:
JavaServer Faces 导航模型
JavaServer Faces 导航模型定义了 Web 应用程序在页面之间移动的方式。导航模型包含一个处理程序,该处理程序使用一个包含导航规则的导航配置文件。处理程序响应操作源(如按钮或超级链接)执行的操作事件。
<?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 属性上,则会将组件的值连接到关联的支持 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 上时,将使用组件的
<h:outputText binding="#{MyBean.outputText1}" id="outputText1"
value="Hello World" />
图 8:将组件绑定到支持 Bean 上 绑定后,支持 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> <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 文档中,将处理请求并生成响应的过程称为生命周期。
Java Studio Creator 2 应用程序模型 Java Studio Creator 2 应用程序模型基于 JavaServer Faces 应用程序模型。Java Studio Creator 开发小组大大改进了该应用程序模型,以使其更易于开发者理解并可以在 IDE 中使用。 用户界面支持 Bean 让我们先了解一下支持 Bean。应用程序模型包含一个用于处理用户界面详细信息的支持 Bean 类别。此支持 Bean 类别(称为“用户界面支持 Bean”)用于处理用户界面属性和事件。此类别中有两个支持 Bean:页面 Bean 和页面片段 Bean。 页面 Bean 页面 Bean 表示一个 JavaServer Pages 页面。页面 Bean 包含用于管理一个 Web 页面在服务器端的所有实现逻辑。它不包含验证和转换逻辑。
应用程序模型还支持未关联 JSP 页的支持 Bean。“数据支持 Bean”一节讨论了这些类型的支持 Bean。 让我们先了解一下支持 Bean。应用程序模型包含一个用于处理用户界面详细信息的支持 Bean 类别。此支持 Bean 类别(称为“用户界面支持 Bean”)用于处理用户界面属性和事件。此类别中有两个支持 Bean:页面 Bean 和页面片段 Bean。 由于 JSP 页和页面 Bean 之间具有一对一的关系,因此,每次将 UI 组件添加到 IDE 可视设计器时,将在 UI 组件的关联页面 Bean 中添加一个属性。由于应用程序模型使用值绑定和组件实例绑定,因此,IDE 通过为组件实例绑定创建 getter 和 setter 并指定缺省值绑定来实现应用程序模型。图 14 显示了将基本静态文本组件放到 Page1 的可视设计器视图上时 IDE 创建的 JavaServer Pages 脚本。请注意对
<?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 中为
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 组件(如下拉列表或文本框)支持值更改事件。引发操作的组件(超级链接或按钮)支持操作事件。 图 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 中的方法绑定表达式方法名称相匹配。
<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 标准请求处理生命周期中插入逻辑。
图 21 显示了 JavaServer Faces 请求生成 JavaServer Faces 响应时覆盖标准请求处理生命周期的 Java Studio Creator 2 应用程序模型生命周期。在图 20 和图 21 中,黄色框表示应用程序模型插入的生命周期方法。
在页面 Bean 构造函数完成后,将在恢复视图阶段结尾调用
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 方法 在发生实际页面呈现之前,将在呈现响应阶段开始时为要呈现的页面调用 要利用 Java Studio Creator 2 应用程序模型生命周期,您应该了解每个阶段在 JavaServer Faces 标准请求处理生命周期中执行的任务。本节简要介绍了标准生命周期中的每个阶段。有关更详细的解释,请参阅 JavaServer Faces 规范。
页面片段 Bean 除了插入的方法外,页面片段 Bean 和页面 Bean 是相同的。页面片段 Bean 只包含 在 Java Studio Creator 2 应用程序模型中,Portlet 页面和 Web 应用程序页面除了页面生命周期存在差异外都是相同的。要充分了解这些生命周期差异,您应该先了解门户如何与 Portlet 进行交互。JSR-168 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 实例变量。
让我们看一下 Portlet 生命周期的图表以了解它是如何影响页面呈现的。假定页面 Bean 包含一个静态文本字段和一个按钮,如图 24 所示。
此外,还假定您在页面 Bean 上定义了一个名为 myString 的属性,并将静态文本字段绑定到该属性上。如果按钮操作方法包含图 25 中所示的代码,在运行 Portlet 应用程序并按下该按钮时,静态文本字段将为空白。
public String button1_action() {
this.myString = "Hello World";
return null;
}
图 25:值更改事件页面 Bean 方法 通常,您希望静态文本字段包含短语 Hello World,而此处并不是这种情况。按下按钮时,按钮操作方法将设置 数据支持 Bean 与用户界面支持 Bean 不同。数据支持 Bean 用于管理数据,它与任何 JSP 页都没有关联。请求 Bean、会话 Bean 以及应用程序 Bean 都是数据支持 Bean。 数据支持 Bean 范围和生命周期 由于数据支持 Bean 与 JSP 页没有关联,因此,数据支持 Bean 的生命周期与页面 Bean 不同。由于这些支持 Bean 用于管理数据,因此,您需要了解它们的范围。Java Studio Creator 2 应用程序模型为数据支持 Bean 定义了三个范围:应用程序范围、会话范围和请求范围。
所有数据支持 Bean 都是由 JavaServer Faces 受管 Bean 工具管理的。该工具仅当需要时才会创建数据支持 Bean,这种方法称为“延迟实例化”。以下介绍了这种延迟实例化的工作方式。 在恢复视图生命周期阶段调用 在使用这种延迟实例化时,使用应用程序模型的开发者不应该依赖于数据支持 Bean 的实例化。即使创建了特定范围,这也并不意味着同样创建了与该范围关联的支持 Bean。因此,应该避免将任何所需的实例化代码放在数据支持 Bean 构造函数中,而是将所有实例化代码放在支持 Bean 的 应用程序 Bean 应用程序 Bean 是在应用程序范围内创建的,用于在应用程序级别管理数据。应用程序 Bean 包含两个生命周期方法: 将应用程序 Bean 作为属性添加到 Servlet 上下文时,将调用 将应用程序 Bean 作为属性从 Servlet 上下文中删除时,将调用
会话 Bean 会话 Bean 是在会话范围内创建的,用于在会话级别管理数据。会话 Bean 包含四个生命周期方法: 将会话 Bean 作为属性添加到会话时(在值绑定表达式中计算会话 Bean 名称的值时将发生这种情况),将调用 会话处于钝化状态时(即暂时将其状态保存到数据存储中),将调用 在激活会话时将调用 将会话 Bean 作为属性从会话中删除时,将调用
请求 Bean 请求 Bean 是在请求范围内创建的,它包含两个生命周期方法: 如果将 UI 组件属性绑定到请求 Bean 上,则会从 JavaServer Faces 标准请求处理生命周期的恢复视图阶段调用 在 JavaServer Faces 标准请求处理生命周期的呈现响应阶段结尾,将调用 用户界面组件 Java Studio Creator 2 应用程序模型包含四组用户界面组件:基本组件、JavaServer Faces 标准组件、定制组件以及非可视组件。
将应用程序模型应用于 Web 应用程序 了解 Java Studio Creator 2 应用程序模型的最佳方式是查看工作示例。样例应用程序 Corporate Travel Center 可以帮助您了解应用程序模型。下载样例。将文件解压缩到 CorporateTravelCenter 目录中,并在 Java Studio Creator 2 IDE 中打开此项目。在 IDE 中打开此项目后,查看“项目”窗口。请参见图 26。
请注意显示在“Web 页”节点下面的具有 .jsp 文件扩展名的文件。这些文件是用户界面的 JSP 页。双击 Page1.jsp,以在可视设计器中打开 Page1。打开此文件后,您应该会在可视设计器编辑工具栏中看到三个按钮(“设计”、"JSP" 和 "Java")。请参见图 27。单击 "Java" 按钮,将打开 Java 编辑器,并显示 Page1 页面 Bean 的源代码。请记住,在 Java Studio Creator 2 Web 应用程序中,每个 JavaServer Pages 页都有一个页面 Bean。
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 调用一次 此样例中的
/**
*<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 方法
/**
* <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 方法 请注意, 最后,让我们看一下此示例中的值绑定。单击可视设计器编辑工具栏中的 "JSP" 按钮(如图 27 所示),以打开 Page1.jsp 的 JavaServer Pages 脚本。尤其是,请注意下拉列表
<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 脚本 由于
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 开发者资源。 |
| |||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||