【问题标题】:Saving data to session in JSF将数据保存到 JSF 中的会话
【发布时间】:2010-11-19 21:58:08
【问题描述】:

我是 J(2)EE 和 Web 应用程序开发领域的新手,但我很快就掌握了它并学到了很多东西。每一天对我来说都是一次奇妙的新发现之旅。

我目前正在开展一个项目,我在 Glassfish v2 上使用 Visual JSF Woodstock。我对 JSF 也很陌生。

有时我需要在请求之间保存一些对象(例如 MyObject)。从我到目前为止所阅读和理解的内容来看,我需要使用会话在不同请求之间保存这些对象。到目前为止一切顺利。

究竟如何做到这一点是我关心的地方。我知道在 JSP 中您可以使用session.setAttribute("myObj", myObject),它会使用 cookie 或 url 重写或隐藏的表单变量将对象保存在客户端。

另一方面,在 JSF 中,我使用 Session 范围的 bean,例如 SessionBean1,并将对象保存为 SessionBean1 属性(例如SessionBean1.setSomeOjb(myObj))。这是解决这个问题的正确方法吗?

我猜测这样做会导致服务器端的内存利用率增加,因为每个请求都会创建会话范围 bean 的新实例 SessionBean1 加上 SessionBean1 中保存的 myObject 实例使用的内存。

我读到您可以使用FacesContext.getExternalContext().getSession/getSessionMap(),它将会话变量保存在客户端。

那么您建议我使用哪种方法 - 会话范围的 bean 或 sessionmap 来保存对象以便在会话请求之间进行访问?

谢谢。

【问题讨论】:

  • “每个请求都会创建一个会话范围 bean 的新实例”这是完全错误的

标签: jsf session-state session-variables


【解决方案1】:

一般而言,Java EE Web 应用程序往往不希望在客户端保存会话数据。您担心服务器端的会话膨胀是对的,一个常见的问题是会话占用量很大,这可能会导致严重的资源和性能问题,尤其是在集群环境中。

我想知道你在哪里看到

我读到您可以使用 FacesContext.getExternalContext().getSession/getSessionMap() 将会话变量保存在客户端。

我相信(在这一点上纠正我)这只是提供对 HttpSession 对象的访问权限,然后您可以在其上使用相同的对象

 session.setAttribute("myObj", myObject)

这本身并不将对象发送回客户端,它保存在服务器中并由一些会话标识符作为键,通常在 cookie 中传递。

现在还有另外两种技术:您可以明确选择将数据放入您自己制造的 cookie 中 - 您可以从 JSF 或 JSP 访问的 servlet API 可以让您这样做,或者您可以使用隐藏字段表单,并因此传递 aorund 会话数据。

但是考虑一下。我使用的 App Server 的经验法则是 1k-4k 数量级的 HttpSession 往往不是问题。比这更大(我已经看到以兆字节为单位的会话)确实对基础设施造成了压力。如果您担心这种大小的会话,您是否希望在每次请求时将 cookie 或隐藏字段中的兆字节数据发送回浏览器?连1k-2k都可能有点大了。

所以建议:

  1. 保持简单。使用 Session API 或其 JSF 表现形式。

  2. 控制会话中的数据量。

在回答有关集群的问题时添加:

通常,在集群环境中,我们有会话亲和性,因此请求会被发送回同一个集群成员。但是,当请求转到不同的服务器时,我们仍然需要考虑这种情况(也许如果集群成员失败)。

一些应用服务器供应商提供会话复制,或者通过直接的服务器间通信或通过将会话持久化到数据库 - 显然这里有开销,所以有时,对于低价值会话,我们只是接受会话丢失,以防万一失败。

有一种说法是,如果会话数据具有很高的价值,那么它应该由应用程序持久化,它实际上是业务数据,应该这样对待。越来越多的 NOSQL 数据库(如 Cloudant 或 MongoDb)用于此目的。在这种情况下,我们可以将 HTTP 会话视为缓存,因为知道在发生错误时可以检索会话数据。

所以我认为购物车可能对企业具有相当大的价值;它代表了客户对他们想花钱的东西的深思熟虑的积累。所以它应该被持久化,而不是仅仅保留在会话中。一旦我们决定坚持它,我们就会发现它会导致其他有趣的场景,例如跨许多客户端设备的整合体验。客户开始在家中使用台式电脑购物,但在网上完成购买。

所以还有一个原则:

3)。不要仅仅因为它存在就过度使用 HTTP 会话。考虑数据的商业价值以及是否应该将其持久化。

【讨论】:

  • 你是对的。我错误地理解为整个会话对象都保存在会话映射中,然后会话映射被发送到客户端,并以 cookie 或隐藏字段的形式存储。相反,它是发送给客户端的会话 ID。如果我理解正确,这个会话 id 可用于检索存储该特定 Web 会话的所有属性的 HttpSession 实例,对吗?感谢您指出我的误解。
  • 您好,我知道您回答这个问题已经很长时间了,但您说了一些关于集群环境的有趣内容。如果会话保存在响应请求的服务器中,如何将会话保持在这种类型的体系结构中?
  • @Kavain:好的 Java-EE 服务器提供了优化集群中会话复制以实现故障转移的选项。它们允许在组中配置服务器,因此并非所有服务器都获得所有会话信息。只有在同一组中的那些。更好地提高复制本身和内存使用的性能。所以例如在 100 台服务器上拥有 1.000.000 个会话大小为 100k 的用户并不意味着每台服务器都需要 100GB 内存。以 3 组为一组分布,将要求每台服务器只有 3 GB。并且 100k 的会话大小很大......
  • @djna:很好的解释。我对 Session 如何工作的理解有点错误。前段时间看了一些文章(其中一篇文章:brockallen.com/2012/04/07/think-twice-about-using-session-state)说为什么我们应该在使用Sessions之前思考一下,可能我理解错了,但现在我相信我明白了。谢谢。
【解决方案2】:

我正在和我的同事一起研究大学的一个项目,它是一个类似于 GoogleImage Labeler 的网络。 好吧,所以我们有一个 UserController,它的方法有 login、logout 等......我们的 session 范围是这样的:

@ManagedBean(name = "userController")
@SessionScoped

好的,这就是 NetBeans 向导为您创建的内容。

我们创建和管理会话的方式是:

register 方法(我们使用 XHTML 中的表单属性...)我们将用户保存在 DB 中,然后我们在会话中添加值:

FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getSessionMap().put("user", current);

其中“当前”是一个用户(当然是登录的用户)。 我们在登录方式处也是如此。

注销方法我们有:

FacesContext.getCurrentInstance().getExternalContext().invalidateSession();

希望对你有帮助。

【讨论】:

  • 这很笨拙。只需将current 分配为UserController 的属性(因为它已经 在会话范围内!)并在注销时将其设置为null(或者更好,执行ExternalContext#invalidateSession()。另见@ 987654321@
  • 我已经编辑了这篇文章,用@BalusC 所说的替换了我的代码。我已将 FacesContext context = FacesContext.getCurrentInstance(); context.getExternalContext().getSessionMap().remove("user"); 更改为 FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
【解决方案3】:

那么您建议我使用哪种方法 - 会话范围的 bean 或 sessionmap 来保存对象以便在会话请求之间进行访问?

这两件事将数据存储在完全相同的位置。

<managed-bean>
  <managed-bean-class>foo.Bar</managed-bean-class>
  <managed-bean-name>bar</managed-bean-name>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

一旦在表达式中被引用,您就可以通过external context 以编程方式查找“栏”。

仅供参考:在 JSF2 中,可以删除声明并用注释替换:

@ManagedBean(name="bar") @SessionScoped
public class Bar {
...

【讨论】:

  • 必须承认我接触 JSF 已经 3 年了。那时我只会使用一个会话范围的托管 bean,就像你展示的那样。当时我没有考虑任何替代方案。我想一个问题可能是如何确保我们保持整洁,不想逐渐积累许多会话范围的托管 bean。这可能要归功于精心选择的 bean 名称。
猜你喜欢
  • 1970-01-01
  • 2013-07-11
  • 2016-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
相关资源
最近更新 更多