在本教程中,您将使用 Sun Java Studio Creator 集成开发环境 (Integrated Development Environment, IDE) 创建一个应用程序,用于说明如何使用应用程序范围、会话范围和请求范围来管理应用程序的对象。范围指的是对象的可用性(或上下文)及其在 Web 应用程序中的预定生命周期。 您在本教程中创建的 Web 应用程序使用一个应用程序范围对象来统计投票,使用一个会话范围对象来确保每个会话期间每个用户只能投票一次。该应用程序还使用请求范围对象来显示用户提交投票的时间。此提交时间存储在请求范围中,因为在将响应发送到客户端浏览器之后,应用程序就不再需要该值。 |
|
目录
|
![[spacer]](/im/a.gif) |
 |
关于范围
只要用户停留在某个页面上,系统就会记住其组件值和页面 Bean 属性值,即使重新显示该页面(例如,用户单击某个按钮后返回 null)也是如此。但是,当用户离开该页面后,其组件值和页面属性值就会消失。
要使其他页面可以访问这些值,或者当用户返回同一页面时可以访问这些值,则需要对它们进行存储。在 IDE 中创建项目时,IDE 会创建三种用于存储这些值的受管 Bean:
RequestBean1
SessionBean1
ApplicationBean1
下图显示了包含缺省受管 Bean 的“概要”窗口。
图 1:缺省受管 Bean |
受管 bean 是 JavaBeans 对象,JavaServer Faces Web 应用程序对该对象进行实例化并将其存储在请求范围、会话范围或应用程序范围中。正如您所猜测的那样,Web 应用程序将 RequestBean1 存储在请求范围中,将 SessionBean1 存储在会话范围中,并将 ApplicationBean1 存储在应用程序范围中。
要向其中一个受管 Bean 添加属性,请在“概要”窗口或“项目”窗口中右键单击该 Bean,然后从弹出式菜单中选择“添加”>“属性”。完成对话框并单击“确定”后,IDE 会将该属性及其相应的 getter 和 setter 方法添加到源代码中。您可以通过以下两种方法访问 Bean 属性的值:将组件的属性绑定到该 Bean 属性,或者调用该 Bean 属性的 getter 和 setter 方法。我们将在本教程中用到这两种访问方法。
创建用于存储值的 Bean 属性之前,必须确定要放置该属性的范围。由于多个用户可以同时访问一个 Web 应用程序,因此要尽可能使用最小的范围,以便不浪费宝贵的空间。下图显示了每种类型的范围所持续的时间。
- 应用程序的范围将一直持续到服务器停止应用程序为止。使用相同应用程序映射的每个会话以及每个请求都可以访问存储在应用程序 Bean 中的值。
- 会话范围从用户第一次访问 Web 应用程序中的页面开始,直至由于不活动而导致会话超时而结束,或者直至 Web 应用程序使会话失效(例如,通过调用
session.invalidate())而结束。
- 请求范围从用户提交页面开始,直至完全呈现响应(不论是哪个页面)而结束。
图 2:Web 应用程序范围 |
假设您所创建的 Web 应用程序包含一个度量单位(像素、厘米和英寸)的下拉列表。您可能需要在 ApplicationBean1 中存储选项列表,以便让所有并行的用户会话都可以共享该列表。另一方面,您可能还需要在 SessionBean1 中存储用户的登录名,该登录名适用于用户在此会话中访问的所有页面。如果您不需要了解有关当前请求的生命周期之外的信息,则可以通过将属性放在 RequestBean1 中来节省空间。
警告:如果将 <redirect> 元素包含在导航规则的 <navigation-case> 元素中,则不能使用请求 Bean。(在页面导航编辑器中单击“源”按钮时,可以看到这些规则。)提交页面时,<redirect> 元素会重定向该页面并结束请求,接下来,后续的页面才能使用请求 Bean 中所存储的任何值。此外,您也不能在 Portlet 中使用请求 Bean。
在 IDE 中创建页面时,页面 Bean 的 Java 源代码包含用于访问 RequestBean1、SessionBean1 和 ApplicationBean1 对象的方法。要访问这些受管 Bean 的属性,请使用类似于以下代码片段中的语句代码。
| 代码样例 1:访问应用程序 Bean 属性 |
ApplicationBean1 appBean = getApplicationBean1();
Option[] choices = appBean.BallotOptions(); |
Web 应用程序将在页面第一次(在 Bean 的范围内)访问受管 Bean 中的属性时,实例化该受管 Bean。例如,只有当用户访问了引用 SessionBean1 属性的页面时,用户会话中才会出现 SessionBean1 对象的实例。
提示:要添加其他的受管 Bean,请在“项目”窗口中右键单击“源包”节点,选择“新建”>“文件/文件夹”,选择“受管 Bean”类别,选择范围,然后完成其余的向导步骤。
向受管 Bean 添加属性
此 Web 应用程序中的页面需要访问以下值:
ballotOptions。此数组包含选票的选项。由于此列表对于所有会话来说都是相同的,因此该属性位于应用程序范围中。
tally。由于计票统计的是所有的会话投票,因此该属性必须位于应用程序范围中。
hasVoted。此布尔型属性跟踪用户是否已投票。由于应用程序需要在多个请求中保留该值,因此应用程序将其存储在会话范围中。
timestamp。用户提交投票时,应用程序会记录时间以备下一个页面使用。由于在呈现下一个页面后应用程序不再需要该值,因此应用程序将其存储在请求范围中。
请完成以下步骤,以便向受管 Bean 中添加属性。
- 创建一个新项目并将其命名为
Scopes。
- 在“概要”窗口中,右键单击 "ApplicationBean1" 节点,然后从弹出式菜单中选择“添加”>“属性”。
将出现“新建属性模式”对话框,如下图所示。
 图 3:“新建属性模式”对话框 |
- 按照以下所示,设置属性的“名称”和“类型”,然后单击“确定”。
 |
名称 |
ballotOptions
|
类型 |
Option[]
|
- 使用相同的步骤,向应用程序 Bean 添加另一个属性。按照以下所示,设置属性的“名称”和“类型”,然后单击“确定”。
 |
名称 |
tally
|
类型 |
HashMap
|
- 在“概要”窗口中,双击 "ApplicationBean1" 节点以打开其 Java 源文件。
- 在源代码中单击鼠标右键,然后从弹出式菜单中选择“修复导入”。
由于多个包中含有 Option 类,因此将出现“修复导入”对话框。
- 从“全限定名称”下拉列表中选择 "com.sun.rave.web.ui.model.Option",然后单击“确定”。
IDE 将在源代码中添加以下类的 import 语句:
com.sun.rave.web.ui.model.Option
java.util.HashMap
- 滚动到
init 方法,然后在该方法的底部添加以下代码。
代码样例 2:向应用程序 Bean 的 init 方法中添加的代码 |
// populate ballot items
ballotOptions = new Option[] {
new Option("java", "Java Programming Language"),
new Option("cpp", "C++"),
new Option("fortran", "Fortran")
};
// initialize counters for ballot choices
tally = new HashMap();
for (int i=0; i < ballotOptions.length; i++) {
this.tally.put(ballotOptions[i].getValue(), "0");
} |
- 在该文件的结尾(紧邻最后一个右花括号之前)添加以下方法。
| 代码样例 3:应用程序 Bean 投票计数方法 |
/**
* Vote counter for property tally.
*/
public void incrementTallyFor(String category) {
int count = getTallyFor(category);
count++;
this.tally.put(category, Integer.toString(count));
}
/**
* Getter for value in property tally.
* @param category HashMap key
* @return Value to which the specified key is mapped
*/
public int getTallyFor(String category) {
String stringCount = (String) this.tally.get(category);
if (stringCount == null) {
return 0;
} else {
int count = Integer.valueOf(stringCount).intValue();
return count;
}
}
|
- 按 Ctrl-S 组合键保存所做的更改,然后按 Ctrl-F4 组合键关闭该文件。
- 在“概要”窗口中,右键单击 "SessionBean1" 节点,然后从弹出式菜单中选择“添加”>“属性”。
如果“概要”窗口未打开,请单击编辑区域中的 "Page1" 标签,然后单击编辑工具栏中的“设计”。当 IDE 处于设计模式时,将会出现“概要”窗口。
- 按照以下所示,设置属性的“名称”和“类型”,然后单击“确定”。
 |
名称 |
hasVoted
|
类型 |
boolean
|
- 在“概要”窗口中,双击 "SessionBean1" 节点以打开其 Java 源文件。
- 滚动到
init 方法,然后在该方法的底部添加以下代码。
代码样例 4:向会话 Bean 的 init 方法添加的代码 |
setHasVoted(false); |
- 按 Ctrl-S 组合键保存所做的更改,然后按 Ctrl-F4 组合键关闭该文件。
- 在“概要”窗口中,右键单击 "RequestBean1" 节点,然后从弹出式菜单中选择“添加”>“属性”。
- 按照以下所示设置属性的“名称”和“类型”,然后单击“确定”。
 |
名称 |
timestamp
|
类型 |
java.util.Date
|
- 检查“概要”窗口,以确保请求 Bean、会话 Bean 和应用程序 Bean 中的属性与下图匹配。您可能需要右键单击 "RequestBean1" 节点,然后从弹出式菜单中选择“刷新”以显示 timestamp 属性。
 图 4:请求、会话和应用程序 Bean 属性 |
创建起始页
请按照下列步骤创建下图中所示的页面。此页面用于提交用户的投票。用户完成投票后,提交按钮就会变为禁用状态,从而使用户无法在该会话中再次投票。
图 5:起始页 |
- 将一个“标签”组件从组件面板的“基本”类别拖放到页面的顶部中央,并将其 text 属性设置为
Reader's Poll: What Is Your Favorite Programming Language?
- 将一个“单选按钮组”组件拖放到“标签”组件的下方。
- 在“属性”窗口中,将该“单选按钮组”组件的
id 属性设置为 voteRBGroup。
- 右键单击“单选按钮组”组件,然后从弹出式菜单中选择“绑定到数据”。
将出现“绑定到数据”对话框。
- 在该对话框的“绑定到对象”标签中,选择 "ApplicationBean1" > "ballotOptions",然后单击“确定”。
- 将一个“按钮”组件拖放到“单选按钮组”组件的下方,并将其 text 属性设置为
View Results。
- 在“属性”窗口中,将该“按钮”组件的
id 属性设置为 viewButton。
- 单击
action 属性的省略号 (...) 按钮,从下拉菜单中选择 "viewButton_action",然后单击“确定”。
IDE 将添加 viewButton_action 事件处理程序,该程序返回 null。
- 将一个“按钮”组件拖放到 "View Results" 按钮的右侧,并将其 text 属性设置为
Submit Vote。
- 在“属性”窗口中,将该“按钮”组件的
id 属性设置为 voteButton。
- 单击
disabled 属性的省略号 (...) 按钮。
将会出现一个对话框。
- 在该对话框中,选择“使用绑定”,单击“绑定到对象”,然后选择 "SessionBean1" > "hasVoted",如下图所示。
图 6:绑定 disabled 属性 |
- 双击 "Submit Vote" 按钮。
IDE 将会添加 voteButton_action 事件处理程序,打开该页面的 Java 源代码并显示方法。
- 将该方法的主体替换为下面以粗体显示的代码。
代码样例 5:voteButton_action 方法 |
public String voteButton_action() {
if (voteRBGroup.getSelected() == null) {
return null;
}
// Tallies are kept across all user sessioins
String votedFor = voteRBGroup.getSelected().toString();
getApplicationBean1().incrementTallyFor(votedFor);
// User can only vote one time per session
getSessionBean1().setHasVoted(true);
// Don't need the timestamp after the next request ends
Date now = new Date();
getRequestBean1().setTimestamp(now);
return null;
} |
- 在源代码中单击鼠标右键,然后从弹出式菜单中选择“修复导入”。
-
从“全限定名称”下拉列表中选择 "java.util.Date",然后单击“确定”。
创建结果页
请按照下列步骤创建下图中所示的页面。此页面用于显示当前的计票。用户可以单击 "Refresh Results" 按钮以获得最新的计票,该计票包括自最后一次显示页面以来其他用户提交的投票。
图 7:结果页 |
- 创建一个新页面并将其命名为
Results。
- 将一个“标签”组件拖放到 "Results" 页的顶部中央,并将其 text 属性设置为
Results。
- 将一个“网格面板”组件从组件面板的“布局”类别拖放到“标签”组件的下方。
- 在“属性”窗口中,将该“网格面板”组件的
columns 属性设置为 2。
- 将一个“静态文本”组件拖动到“网格面板”组件中。当该“网格面板”组件的边框变为蓝色实线时,放置“静态文本”组件,如下图所示。
图 8:在“网格面板”组件中放置组件 |
- 在“属性”窗口中,将该“静态文本”组件的
id 属性设置为 descST0。请注意,此 id 的最后一个字符是零,而不是字母 O。
- 将另一个“静态文本”组件拖动到“网格面板”组件上,当该“网格面板”组件的边框变为蓝色实线时,放置“静态文本”组件。
- 在“属性”窗口中,将该“静态文本”组件的
id 属性设置为 countST0。
- 再添加四个“静态文本”组件,并将它们的
id 属性依次设置为以下值:
descST1
countST1
descST2
countST2
“概要”窗口应当如下图所示。否则,请在“概要”窗口中拖放静态文本节点,以便按正确的顺序重新排列它们。
图 9:“网格面板”组件中的“静态文本”组件 |
- 将一个“按钮”组件拖放到“网格面板”组件的下方,并将其 text 属性设置为
Home。
- 将该“按钮”组件的
id 属性设置为 homeButton。
- 单击
action 属性的省略号 (...) 按钮,从下拉列表中选择 "homeButton_action",然后单击“确定”。
- 将一个“按钮”组件拖放到 "Home" 按钮组件的右侧,并将其 text 属性设置为
Refresh Results。
- 将该“按钮”组件的
id 属性设置为 refreshButton。
- 单击
action 属性的省略号 (...) 按钮,从下拉菜单中选择 "refreshButton_action",然后单击“确定”。
- 将一个“静态文本”组件拖放到“按钮”组件的下方。
-
将该“静态文本”组件的
id 属性设置为 messageST,并将其 text 属性保留为空。
- 单击编辑工具栏中的 "Java" 以查看该页面的 Java 源代码。
- 滚动到
prerender 方法并添加下面以粗体显示的代码。
代码样例 6:prerender 方法 |
public void prerender() {
// Display latest poll results
ApplicationBean1 appBean = getApplicationBean1();
Option[] choices = appBean.getBallotOptions();
descST0.setText(choices[0].getLabel());
descST1.setText(choices[1].getLabel());
descST2.setText(choices[2].getLabel());
int i;
i = appBean.getTallyFor(choices[0].getValue().toString());
countST0.setText(Integer.toString(i));
i = getApplicationBean1().getTallyFor(choices[1].getValue().toString());
countST1.setText(Integer.toString(i));
i = getApplicationBean1().getTallyFor(choices[2].getValue().toString());
countST2.setText(Integer.toString(i));
RequestBean1 reqBean = getRequestBean1();
Date timestamp = (Date) reqBean.getTimestamp();
if (timestamp != null) {
messageST.setText("Your vote was recorded at " +
(String)DateFormat.getTimeInstance(DateFormat.LONG).format(
timestamp));
}
} |
- 在源代码中单击鼠标右键,然后从弹出式菜单中选择“修复导入”。
-
对于类名 Option,从“全限定名称”下拉列表中选择 "com.sun.rave.web.ui.model.Option",对于类名 Date,选择 "java.util.Date",然后单击“确定”。
指定页面导航
请按照下列步骤,指定下图中所示的按钮的页面导航。
图 10:“导航”窗口 |
- 在编辑区域中,单击 "Results" 标签,然后单击“设计”以便在可视设计器中查看该页面。
- 右键单击页面中的空白处,然后从弹出式菜单中选择“页面导航”。
- 单击 "Page1.jsp" 图标以将其放大。
- 单击 "voteButton" 并将其拖动到 "Results.jsp"。
- 键入
vote,然后按 Enter 键以命名该链接。
- 单击 "Page1.jsp" 中的 "viewButton" 并将其拖动到 "Results.jsp"。
- 键入
view results,然后按 Enter 键以命名该链接。
- 单击 "Results.jsp" 图标以将其放大。
- 单击 "homeButton" 并将其拖动到 "Page1.jsp"。
- 键入
home,然后按 Enter 键以命名该链接。
运行应用程序
要通过同一个浏览器来启用多个会话,请配置应用程序以便在每个会话一分钟不活动时结束该会话。然后,部署并运行该应用程序。
- 在“文件”窗口中,展开 "Scopes" > "web" > "WEB-INF",如下图所示。
图 11:“文件”窗口 |
- 右键单击 "web.xml",然后从弹出式菜单中选择“编辑”。
- 在该文件的结尾(紧邻
</web-app> 结束标记之前)添加以下标记。
| 代码样例 7:配置会话超时 |
<session-config>
<session-timeout>1</session-timeout>
</session-config> |
- 单击主工具栏中的“运行主项目”按钮。
- 当出现起始页后,选中一个单选按钮,然后单击 "Submit Vote"。
浏览器将显示结果页。请注意,在结果页上将会显示出您提交投票的时间。
- 单击 "Home" 返回到起始页。
由于您已经投票,因此将禁用 "Submit Vote"。
- 单击 "View Results"。
请注意,结果页将不再显示您提交投票的时间。这是因为以前的请求 Bean 超出范围,并且已实例化新的请求 Bean。
- 请等待一分钟,以使会话超时。然后,在浏览器的地址文本框中键入以下 URL 并按 Enter 键启动新的会话:
http://localhost:28080/Scopes。如果未使用缺省的服务器配置,则可能需要将 28080 更改为其他端口。
- 再次投票并检查结果。结果应当包括第一次的投票。
- 如果具有不同的浏览器应用程序,请启动该浏览器,在浏览器的地址文本框中键入
http://localhost:28080/Scopes,然后按 Enter 键。提交另一个投票。
- 在第一个浏览器中,单击结果页中的 "Refresh Results"。
结果应当包括通过第二个浏览器提交的投票。
执行更多操作
利用在本教程中所学到的内容,您可以构建一个提示输入登录名的应用程序。添加一个页面,该页面用于显示已访问此 Web 应用程序的用户的数量(不包括每位用户重复访问的次数)。
小结
可以使用应用程序 Bean、会话 Bean 和请求 Bean 来存储信息以供其他页面使用。
- 可以使用应用程序 Bean 存储适用于所有用户会话的信息,如“下拉列表”组件的静态选项列表。
- 可以使用会话 Bean 存储在整个用户会话期间供其他页面使用的信息,如用户的登录名。
- 如果只需要供下一个页面使用的信息,则使用请求 Bean。
警告:不能在 Portlet 中使用请求 Bean。如果将 <redirect> 元素包括在导航规则的 <navigation-case> 元素中,则不能使用请求 Bean。
在页面访问请求 Bean、会话 Bean 或应用程序 Bean 中的某个属性时,将会实例化这些 Bean。当 Bean 的范围结束时,这些 Bean 将会被销毁。
要向会话 Bean 添加属性,请右键单击“概要”窗口或“项目”窗口中的会话 Bean 节点,然后选择“添加”>“属性”。也可以使用类似的步骤向请求 Bean 或应用程序 Bean 添加属性。
另请参见:
此页的最新修改时间:2006 年 3 月 16 日
|