JSF 2.x @ViewScoped 托管 bean 线程安全吗?
Posted
技术标签:
【中文标题】JSF 2.x @ViewScoped 托管 bean 线程安全吗?【英文标题】:Are JSF 2.x @ViewScoped managed beans thread safe? 【发布时间】:2011-11-16 15:58:15 【问题描述】:我已经在这个问题上搜索了几个小时,但没有评估。
WELD 文档和 CDI 规范对所提供范围的线程安全性非常清楚。
例如:
应用范围 - 不安全
会话范围 - 不安全
请求范围 - 安全,始终绑定到单个线程
对话范围 - 安全(由于 WELD 代理序列化来自多个请求线程的访问)
我在 JSF 2.x 定义的视图范围内找不到任何内容。
它与会话范围大致在同一个桶中,因为尽管它绑定到单个视图/用户,但多个请求很有可能同时到达范围。我不知道 JSF 实现是否序列化了多个请求对 bean 的访问。
任何人都知道规范或 Morraja/MyFaces 实现可以解决这个问题吗?
【问题讨论】:
【参考方案1】:视图范围是正常使用线程安全的。它只能由一个浏览器窗口/选项卡使用。也就是说,它由一个在初始 GET 请求中设置的唯一隐藏输入字段作为键控。同一视图上的每个回发都将使用同一个视图范围 bean。浏览器本身已经在同一窗口/选项卡中“同步”回发请求。新的浏览器窗口/选项卡实际上是一个新的 GET 请求,因此将创建一个新的且完全独立的视图。
对于 ajax 回发,它们按规范排队。这在JSF 2 specification的第13.3.2章中有提到:
13.3.2 Ajax 请求队列
所有的 Ajax 请求必须先放入客户端请求队列,然后再发送到客户端 服务器确保 Ajax 请求按照发送顺序进行处理。一直在队列中等待的请求 最长的是要发送的下一个请求。发送请求后,Ajax 请求回调函数必须移除该请求 从队列中(也称为出队)。如果请求成功完成,则必须将其从队列中移除。如果 发生错误,必须通知客户端,但仍必须从队列中删除请求,以便下一个请求 可以发送。必须发送下一个请求(队列中最早的请求)。参考
jsf.ajax.request
关于 Ajax 请求队列的更多细节的 javascript 文档。
只有在使用 PrimeFaces 时,才能使用 <p:ajax async="true">
禁用排队。当将此与视图范围的 bean 结合使用时,必须重新考虑线程安全性,方法与会话范围的 bean 相同。
另见:
Concurrency of @ApplicationScoped JSF managed beans Help me to understand JSF managed bean scope from concurrency view How to choose the right bean scope?【讨论】:
我知道,但是就像对话范围一样,在任何给定时间都可能有多个正在运行的 ajax 请求,每个请求都会产生一个可以访问视图范围的请求线程.想想有人点击了一个 ajax 按钮导致 bean 中的数据修改的垃圾邮件,如果这些请求的访问没有被代理同步或序列化,那么可能会出现问题。 哦,这边。是的,来自同一视图的 ajax 请求在客户端排队,如 JSF 2 规范中所述。我将使用规范的摘录来更新答案。 JSF 不会对同步请求进行排队,但无论如何都是浏览器执行此操作。 感谢您的更新,如果我可以再为 1 个问题麻烦您:很明显,引用的规范涵盖了 ajax 调用,但是,如果您有一个 jsf 视图的链接,该链接由viewscoped bean,并且该视图具有通过 af:event 调用支持 bean 的方法,例如 preRenderView 事件或其他东西。如果源链接被垃圾邮件点击,我希望多次调用该方法(我认为,我不是 100% 清楚视图范围何时开始),不知道这些调用是否会以 ajax 的方式排队电话是? 也许我要问的会落入同步请求的桶中?我不确定浏览器是否真的将它们排队,但是我已经对我刚刚使用 sessionscoped bean 描述的案例进行了一些测试,看看会发生什么,我肯定会得到并发方法执行。 链接是 GET 请求,每个 GET 请求都会重新创建 bean,即使它在同一个窗口中也是如此。非 null/void 导航结果也将重新创建 bean。【参考方案2】:ViewScoped
bean 存储在为每个UIViewRoot
创建的"view" Map
中。当一个 JSF 运行时处理两个并发请求时,通常不太可能为这些请求创建/恢复相同的 UIViewRoot
实例,因为 HTTP 请求中的 javax.faces.ViewState
表单参数用于确定是否存在现有的 @ 987654328@ 实例是否应该恢复(在回发时)。正如 BalusC 所指出的,两个不同的浏览器窗口将导致创建两个不同的视图范围 bean,因为两个浏览器选项卡的底层 ViewStates 参数不同(如果您发出两个不同的 HTTP 请求并且浏览器正在使用每个显示各个选项卡,而不是使用缓存副本)。
然而,关于线程安全的部分超出了浏览器选项卡/窗口。如果两个 HTTP 请求(因此,两个线程)在请求中呈现相同的 javax.faces.ViewState
值由容器处理。因此,视图范围的 bean 本质上不是线程安全的,它们也不是以线程安全的方式访问的。您可以通过重放具有相同javax.faces.ViewState
值的请求来确认这一点,并观察容器在短时间内收到多个此类请求时容器/JVM 的行为(导致并发访问相同UIViewRoot
的可能性)多线程实例)。
【讨论】:
以上是关于JSF 2.x @ViewScoped 托管 bean 线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章
JSF ViewScoped bean 在所有 ajax 请求中的唯一 ID?