JSF 2.x的@ViewScoped管理的bean是否线程安全?

20

我已经在谷歌上搜索了几个小时,但仍无法解决这个问题。

WELD文档和CDI规范对提供的作用域的线程安全性非常清晰。

例如:

  • 应用程序范围 - 不安全

  • 会话范围 - 不安全

  • 请求范围 - 安全,始终绑定到单个线程

  • 会话期间范围 - 安全(由于WELD代理将多个请求线程的访问进行序列化)

我找不到JSF 2.x定义的View Scope的任何信息。

它与Conversation Scope大致相同,在绑定到单个视图/用户时很可能会出现多个请求同时命中作用域。我不知道JSF实现是否会对来自多个请求的bean进行访问序列化。

是否有了解规范或Morraja/MyFaces实现情况的人可以澄清这一点?

2个回答

20

视图作用域通常在正常使用情况下是线程安全的。它只能被一个浏览器窗口/选项卡使用。也就是说,它由一个唯一的隐藏输入字段作为键,在初始GET请求时设置。在同一视图上的每个postback将使用相同的视图作用域bean。浏览器本身已经“同步”了同一窗口/选项卡中的postback请求。新的浏览器窗口/选项卡实际上是一个新的GET请求,因此会创建一个全新且完全独立的视图。

至于ajax postbacks,根据规范它们是排队的。这在JSF 2规范第13.3.2章节中有提到:

13.3.2 Ajax Request Queueing

所有Ajax请求在发送到服务器之前必须放入客户端请求队列中,以确保按发送顺序处理Ajax请求。等待时间最长的请求是要发送的下一个请求。发送请求后,Ajax请求回调函数必须从队列中删除请求(也称为出队)。如果请求成功完成,则必须将其从队列中删除。如果有错误,则必须通知客户端,但仍必须从队列中删除请求,以便可以发送下一个请求。必须发送下一个请求(队列中最旧的请求)。有关Ajax请求队列的更多详细信息,请参阅jsf.ajax.request JavaScript文档。

只有在使用PrimeFaces时,队列可以通过<p:ajax async="true">禁用。如果与视图作用域bean组合使用,则必须重新考虑线程安全性,就像对于会话作用域bean一样。

另请参阅:

  • 帮我理解JSF托管bean范围的并发视图
  • 如何选择正确的bean范围?

  • 2
    我理解这一点,然而就像对话范围一样,可能会有多个Ajax请求在任何给定时间内进行,每个请求线程都可以访问视图范围。想象一下有人不断点击导致数据修改的Ajax按钮,如果这些请求的访问没有被代理同步或序列化,那么就可能会出现问题。 - Mark
    哦,原来是这样。是的,正如JSF 2规范所述,来自同一视图的ajax请求在客户端被排队等待。我会更新答案并附上规范的摘录。JSF不会排队同步请求,但无论如何都是浏览器在排队。 - BalusC
    谢谢您的更新,如果可以麻烦您再回答一个问题:很明显引用的规范涵盖了ajax调用,但是,如果您有一个链接到由viewscoped bean支持的jsf视图,并且该视图具有通过f:event调用的后备bean中的方法,例如preRenderView事件或其他事件。如果源链接被垃圾邮件点击,我会期望对该方法进行多次调用(我认为,我不是100%确定何时确切地开始视图范围),是否有任何想法这些调用是否会以类似于ajax调用的方式排队? - Mark
    也许我所询问的会归为同步请求的范畴?但我不确定浏览器是否真正将它们排队。我已经对使用sessionscoped bean的情况进行了一些测试,以查看会发生什么,并且我明确地得到了并发方法执行的结果。 - Mark
    链接是GET请求,每个GET请求都会重新创建bean,即使它在同一个窗口中。非空/无效的导航结果也会重新创建bean。 - BalusC
    @BalusC 我有点晚了,但是假设我有一个视图作用域的bean,在页面上我触发一个ajax请求到外部服务(仅使用javascript),当该调用返回时,我从javascript调用p:remoteCommand,它使用bean的方法来更新用户视图。这可能会创建并发问题吗?如果用户在执行remoteCommand actionListner时继续使用bean。 - dssof

    4

    ViewScoped bean被存储在每个UIViewRoot创建的“视图”Map中。当JSF运行时处理两个并发请求时,通常不太可能为这些请求创建/恢复相同的UIViewRoot实例,因为HTTP请求中的javax.faces.ViewState表单参数用于确定是否应恢复现有的UIViewRoot实例(在postback上)。正如BalusC所指出的那样,两个不同的浏览器窗口将导致创建两个不同的视图范围bean,因为对于这两个浏览器选项卡,底层的ViewStates参数是不同的(如果您正在发出两个不同的HTTP请求,并且浏览器正在使用每个请求的响应来显示各个选项卡,而不是使用缓存的副本)。

    然而,关于线程安全的部分超出了浏览器标签/窗口。在JSF运行时内部(至少在Mojarra中),没有内在机制会同步访问UIViewRoot和视图映射,如果两个HTTP请求(因此,两个线程)在请求中呈现相同的javax.faces.ViewState值以由容器处理。因此,视图范围bean天生不是线程安全的,也不是以线程安全的方式访问的。您可以通过重放具有相同的javax.faces.ViewState值的请求,并观察在短时间内容器接收到多个这样的请求时容器/JVM的行为,从而确认这一点(从而导致多个线程同时访问同一个UIViewRoot实例的可能性)。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接