理解作用域和受管Bean

您在本教程中所创建的 Web 应用程序采用 application 范围内的一个对象来统计投票数,采用 session 范围内的一个对象来确保用户在每个会话中只能投票一次。该应用程序采用一个 request 范围内的对象来显示用户提交投票的时间。该时间存储在 request 范围,因为应用程序在响应被发送到客户端浏览器以后不再需要这个值。

本教程需要采用以下技术及资源

JavaServer Faces 组件/

Java EE 平台

1.2 和 Java EE 5*

1.1 和 J2EE 1.4

Travel 数据库 非必须

* 为了利用 NetBeans IDE 6.0 的 Java EE 5 能力,请采用一个完全符合 Java EE 5 规范的应用程序服务器,如 Sun Java Application Server 9/GlassFish 。

此教程已针对 GlassFish v2 应用程序服务器上的应用做出调整。如果您使用的是不同的服务器,请参阅发行说明及常见问题解答以了解已知问题和变通方法。要了解受支持的服务器和 Java EE 平台的信息,请参阅发行说明。

关于作用域

当用户停留在一个页面上的时候,即使页面重新显示,例如用户点击一个返回空值的按钮时,组件的值仍然被存储。然而,用户离开这个页面时,组件的值随即消失。

要让值对其他页面有效,或者对用户应当返回的相同页面有效,你需要存储值。当你从 IDE 里创建一个项目时, IDE 将创建三个 受管 Bean 用来存储值:

RequestBean1

SessionBean1

ApplicationBean1

下图所示的是一个列出了受管 Bean 的“导航”窗口。

图表 1:默认的受管 Bean

一个受管 Bean 就是一个 JavaBean 对象,该对象被 JavaServer Faces web 应用程序所呈现并存储于request、session 或者 application 范围内。Web 应用程序将 RequestBean1 存储于 request 范围内,将 SessionBean1 存储于 session 范围内,并将 ApplicationBean1 存储于 application 范围内。

要向这些受管 Bean 添加属性,您可以右击“导航”窗体中的一个 bean 并在源代码中键入属性声明。右击 Java 编辑器中的任意处,然后选择“重构” > “封装字段”。然后从列表中选择属性并勾选要添加的方法,点击“构造”。

在创建一个 bean 属性来存储值之前,您必须决定属性值的适用作用域。因为许多用户可能在同一时间访问 web 应用程序,您需要使用尽可能最小的作用域以便对服务器资源进行最好的利用。下图展示了每种作用域的持续期。

Application 作用域将持续到服务器停止该应用程序为止。您存储在 application bean 中的值对于使用同一应用程序映射的每个会话和每个请求都是有效的。

Session 作用域在用户第一次访问 web 应用程序中的某一页面时开始,当用户的会话由于长时间处于非活动状态而超时,或者当 web 应用程序令会话无效时,例如通过调用 session.invalidate(),会话终止。

Request 作用域在用户提交页面时开始,当响应彻底完成时终止,不论返回的页面是什么。

图表 2: Web 应用程序的作用域

例如, 您的 web 应用程序有一个用于选择测量类型的下拉列表(像素、厘米和英寸)。您可能要将选项列表存储于 ApplicationBean1 中,以便当前所有的用户会话可以共享这个列表。另一方面,您可能将用户登陆名存储于 SessionBean1 中,以使该用户名对该用户在此会话中所访问的任何页面都有效。如果对于当前请求您不需要其生命周期以外的信息,您可以通过将属性置于 RequestBean1 中来节约空间开销。

警告: 如果您已在一个导航规则的 元素中包含 元素,您将不能使用 request bean 。(您可以在页面流编辑器中点击 XML 按钮来查看这些规则。)当页面被提交时, 元素将重定向页面并在后面的页面能够使用任何存储于 Request Bean 中的值之前结束请求。

当您从 IDE 中创建一个页面时,页面 bean 的 Java 源代码中就包含了访问 RequestBean1 、 SessionBean1 和 ApplicationBean1 对象的方法。要访问这些属性的受管 bean ,您可以使用和下面的代码片段相似的代码。

代码示例 1 :访问一个 Application Bean 属性

ApplicationBean1 appBean = getApplicationBean1();Option[] choices = appBean.BallotOptions();

当一个页面在某个受管 bean 的作用域内第一次访问其属性时, web 应用将该受管 Bean 实例化。例如,一个 SessionBean1 对象实例直到用户访问一个引用了 SessionBean1 中某个属性的页面才存在于用户会话中。一个 ApplicationBean1 的对象实例将在页面首次通过任意会话访问 application bean 时被创建,并将伴随应用程序的运行而存在。

技巧: 添加额外的受管 Bean :

在“项目”窗口中,展开“源包”节点,右击包节点。

选择“新建” > “其它”,选择 JavaServer Faces 类别并选择“文件类型”下的作用域,然后单击“下一步”。

键入新建受管 Bean 的文件名并点击“完成”。

新建的受管 Bean 出现在“项目”窗口中的“源包”节点下。

向受管 Bean 添加属性

该 web 应用中的页面需要访问以下值,在本节中您将创建它们:

ballotOptions.一个包含投票选项列表的数组属性。因为该列表对任何会话来说都是相同的,所以该属性属于 application 作用域。

tally. 一个 hash map 属性,用于记录来自各个会话的投票。由于该属性必须在几个会话之间都存在,所以它也属于 application 作用域。

hasVoted.一个布尔型属性,用于跟踪用户是否已经投票。由于应用程序需要在一个会话中的几个请求内保存这个值,所以将这个值存在该应用的 session 作用域。

timestamp. 一个 Date 类型的属性,应用程序将用户提交投票的时间记录在其中,以便下一个页面可以调用。由于应用程序在下一个页面传输给浏览器后不再需要这个值,所以该属性存在于 request 作用域内。

完成以下步骤,向受管 Bean 添加属性。

从主菜单选择“文件” > “新建项目”。

在“新建项目”向导中,从“类别”列表中选择 Web 并从“项目”列表中选择 Web Application ,之后点击“下一步”。

将项目命名为 Scopes 并点击“下一步”。

在“框架”面板中,勾选 Visual Web JavaServer Faces 并点击“完成”。

Scopes 项目的 Page1.jsp 在“可视化设计器”中被打开。

在“导航”窗口中,双击 ApplicationBean1 。

该操作将打开 ApplicationBean1.java 以便进行编辑。

接下来您可以添加 Application bean 属性。在构造函数 public class ApplicationBean1 extends AbstractApplicationBean下面,添加如下声明:private Option[] ballotOptions;private HashMap tally;

在 Java 编辑器中右击并选择“修复导入”。由于不止一个包内含有 Option 类,所以 IDE 将弹出“修复导入”对话框。

对 Java EE 5 项目,选择 com.sun.webui.jsf.model.Option。

对 J2EE 1.4 项目,选择 com.sun.rave.web.ui.model.Option。

在 Java 编辑器中右击并选择“重构” > “封装字段”。

在“封装字段”对话框中,勾选 ballotOptions 和 tally 属性的 getter 和 setter 方框,以创建相应的 getter 和 setter 方法,如下图所示。

图表 3: “封装字段”对话框

滚动到 init 方法并将下面以粗体显示的代码添加到该方法的尾部。

代码示例 2: 向 init 方法中添加的代码

// TODO - add your own initialization code here     // 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: Application 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 。 该操作将打开 SessionBean1.java 以便进行编辑。

注意:如果“导航”窗口没有打开,点击编辑区的 Page1 标签,并单击编辑工具栏中的“设计”。“导航”窗口将在 IDE 处于设计模式时出现。

接下来您可以添加 Session bean 属性。在构造函数 public class SessionBean1 extends AbstractSessionBean 下添加如下声明:private boolean hasVoted;

在 Java 编辑器中右击并选择“重构” > “封装字段”。

在“封装字段”对话框中,勾选 hasVoted 属性中的 getter 和 setter 方框以创建相应的 getter 和 setter 属性,并点击“重构”。

滚动至 init 方法并将以下粗体显示的代码添加到该方法尾部。

代码示例 4: 要向 Session Bean 的 init 方法中添加的代码

// TODO - add your own initialization code here     setHasVoted(false);

按 Ctrl+S 以保存您做出的更改,并按 Ctrl+F4 以关闭该文件。

在“导航”窗口中,双击 RequestBean1 。 该操作将打开 RequestBean1.java 以便进行编辑。

添加 Request bean 属性。在构造函数 public class RequestBean1 extends AbstractRequestBean下添加如下声明:private java.util.Date timestamp;

在 Java 编辑器中右击并选择“重构” > “封装字段”。

在“封装字段”对话框中,勾选 timestamp 属性中的 getter 和 setter 方框以创建相应的 getter 和 setter 属性,并点击“重构”。

点击 Page1 标签然后点击 Page1 的 Design 按钮。检查“导航”窗口以确保 request bean 、 session bean 、 以及 application bean 中的属性与下图所示相一致。

图表 4: Request Bean 、 Session Bean 、 以及 Application Bean 的属性

创建起始页

按本节中的以下步骤创建一个如下图所示的在浏览器中运行的页面。如果用户点击 Submit Vote 按钮,则页面提交用户的投票。一旦用户已经投票,按钮就变为禁用以防止用户在同一会话内再次投票。

图表 5: Page1

在编辑工具栏中点击 Page1

从“面板”的“基本组件”中拖一个“标签”到页面顶部正中,并将标签的文本设置为 Reader’s Poll: What Is Your Favorite Programming Language?

在该“标签”组件下面放一个“单选按钮组”组件。

在“属性”窗口中,将该组件的 id 设置为 voteRBGroup。

右击“单选按钮组”组件并从弹出菜单中选择“绑定到数据”。

此时将出现一个“绑定到数据”对话框。

在该对话框中的“绑定到对象”标签中,选择 ApplicationBean1 > ballotOptions 并点击“确定”。

在“单选按钮组”组件下方放置一个“按钮”,并将其文本设置为 View Results 。

特别注意: 在 IE7 中存在一个已知的问题,可以影响到 JSF 1.2 按钮组件的宽度。工作区中应当将按钮组件放置在一个布局组件(网格面板、组面板或者布局面板)中。调整布局组件的大小将自动调整按钮组件的大小。

在属性窗口中,将 id 设为 viewButton 。

点击 action 属性的省略号按钮 ,从下拉列表中选择 viewButton_action 并点击确定。

IDE 将添加 viewButton_action 事件处理器,其返回值为 null 。

在 View Results 按钮的右边放置一个“按钮”组件,并将其文本设置为 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 sessions     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

在“项目”窗口中,右击您的 Scopes 项目下的 Web 页节点,选择“新建” > “Visual Web JSF 页”,将该页面命名为 Results ,并点击“完成”来创建页面。

在 Results 页面的顶部中间放置一个“标签”组件,并将其文本设为 Results 。

在该“标签”组件的左边放一个“按钮”组件并将其文本设置为 Home 。

将该“按钮”组件的 id 设置为 homeButton 。

点击 action 属性后的省略号按钮 ,从“处理程序”下拉列表中选择 homeButton_action 并单击“确定”。

在该“标签”组件的右边放一个“按钮”组件,并将其文本设置为 Refresh Results 。

将该“按钮”组件的 id 设置为 refreshButton 。

点击 action 属性后的省略号按钮 ,从“处理程序”下拉列表中选择 refreshButton_action 并点击“确定”。

从“组件面板”的“布局”区域内拖一个“网格面板”组件到上述“标签”组件的下方。

在“属性”窗口,将 cellspacing 属性设置为 10 并将 columns 属性设置为 1 。

拖一个“静态文本”组件到上述“网格面板”组件内。当“网格面板”组件的边框变为蓝色实线时,放开“静态文本”组件,如下图所示。

图表 8: 向“网格面板”组件中拖放一个组件

在“属性”窗口中,将“静态文本”组件的 id 设置为 resultsST 。 将其 text 属性留空即可。

反选 escape 属性的方框。

然后,您可以向该组件的 text 属性内添加 HTML 代码。将 escape 属性设为 false 后,HTML 代码将原样输出到浏览器。

再拖一个“静态文本”组件到上述“网格面板”组件内。并当“网格面板”组件的边框变为蓝色实线时,放开“静态文本”组件。

将该“静态文本”组件的 id 设置为 messageST 。将其 text 留空。

点击编辑工具栏上的 “Java” 并查看该页面的 Java 源代码。

在“导航”窗口中,双击 prerender 方法将其在 Java 编辑器中打开,然后添加下面粗体所示的代码。

代码示例 6: prerender 方法

public void prerender() {    // Display latest poll results    ApplicationBean1 appBean = getApplicationBean1();    Option[] choices = appBean.getBallotOptions();    String str = "";    for (int i = 0; i < choices.length; i++) {     int count =      appBean.getTallyFor(choices[i].getValue().toString());     str = str + "" +      choices[i].getLabel() +      "" +      count +      "";    }    str = str + "";    resultsST.setText(str);    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));    }   }

这段代码将创建一个包含所有投票总数的 HTML 表格,并将这个 HTML 表格放在第一个“静态文本”组件的 text 属性中。如果用户刚投过票,第二个“静态文本”组件将显示投票被注册的日期和时间。

在源代码中右击并从弹出菜单中选择“修复导入”。

从 Date 下拉列表中,选择 java.util.Date 。

根据当前项目所使用的 Java EE 版本,在 Option 下拉列表中进行以下操作:

对于 Java EE 5 项目,选择 com.sun.webui.jsf.model.Option 。

对于 J2EE 1.4 项目,选择 com.sun.rave.web.ui.model.Option 。

定义页面导航

按以下步骤为按钮定义页面导航,如下图所示。

图表 9 : “页面流”编辑器

在编辑区,点击 Results 标签并点击 Design 以便在可视化编辑器中查看该页面。

在该页面的空白处右击并从弹出菜单中选择“页面导航”。

faces-config.xml 就显示在“页面流”编辑器中。

点击 Page1.jsp 图标中的加号来展开该图标。

在 viewButton 处单击并拖动到 Results.jsp ,将在该按钮和 Results 页面之间创建一个连接器。

双击连接器的标签使其进入编辑模式,键入 view results 然后按回车。

在 voteButton 处单击并拖动到 Results.jsp 。

双击连接器的标签使其进入编辑模式,键入 vote 然后按回车。

点击 Results.jsp 图标中的加号使之展开。

在 homeButton 处单击并拖动到 Page1.jsp。

双击连接器的标签使其进入编辑模式,键入 home 然后按回车。

运行应用程序

要从一个浏览器启用多个会话,需要将应用程序设置改为在每个会话处于非活动状态1分钟后将其结束。然后部署并运行该应用程序。

在“文件”窗口内,展开 Scopes > web >WEB-INF ,如下图所示。

图表 10: “文件”窗口

双击 web.xml 可在编辑器中打开该文件。

在“会话超时”文本框中键入 1 ,如下图所示。

图表 11:在 web.xml 可视化编辑器中设置会话超时

保存并关闭该文件。

点击主工具栏中的“运行主项目”按钮 。

当起始页出现的时候,选择一个单选按钮并点击 Submit Vote 。

浏览器显示出结果页。注意结果页已经显示了您提交投票的时间。

单击 Home 即返回到起始页。

由于您已经投过票, Submit Vote 是禁用的。

点击 View Results 。

注意结果页不再显示您投票的时间。这是因为上一个请求 bean 已经超出作用域而一个新的请求 bean 已经被示例化。

等待1分钟使会话超时。然后在浏览器的地址栏输入以下 URL 并按下回车来启动一个新会话:http://localhost:8080/Scopes 。 如果没有使用默认的服务器配置,您可能需要将 8080 改为其它端口。

再次投票并查看结果。结果中应当包含您的第一次投票。

如果您还有其他浏览器程序,启动那个浏览器,在其地址栏键入 http://localhost:8080/Scopes 并按下回车以完成另一次投票。

在第一个浏览器中,点击结果页中的 Refresh Results 。

结果中应当包含您从第二个浏览器中提交的投票。

实现更多

运用您在本教程中所学到的来创建一个能够提示登录名的应用程序。添加一个页面来显示所有访问该 web 应用的用户总数。

小结

您可以使用 application bean、 session bean 以及 request bean 来存储其它页面需要使用的信息。

使用 application bean 来存储应用于所有用户会话的信息,例如一个用于下拉列表组件的静态的选项列表。

使用 session bean 来存储可被整个用户会话的其它页面所调用的信息,例如用户的登录名。

如果您只需要下一个页面所需的信息,请使用 request bean 。

注意: 如果您在一个导航规则的 元素中包含了 元素,您不能使用 request bean 。

对于 request bean、 session bean 或者 application bean 而言,当一个页面访问它们的一个属性的时候,它们就被实例化了。

要向 Session Bean 中添加一个属性,右击“大纲”窗口中的 Session Bean 节点并选择“添加” > “属性”。采用相同的步骤可以向 Request Bean 或 Application Bean 中添加属性。

鸟的翅膀在空气里振动,那是一种喧嚣而凛裂的,

理解作用域和受管Bean

相关文章:

你感兴趣的文章:

标签云: