如何选择合适的bean范围?

Posted

技术标签:

【中文标题】如何选择合适的bean范围?【英文标题】:How to choose the right bean scope? 【发布时间】:2011-10-25 07:17:01 【问题描述】:

我注意到有不同的 bean 范围,例如:

@RequestScoped
@ViewScoped
@FlowScoped
@SessionScoped
@ApplicationScoped

每个的目的是什么?如何为我的 bean 选择合适的范围?

【问题讨论】:

【参考方案1】:

简介

它代表 bean 的范围(生命周期)。如果您熟悉基本 servlet Web 应用程序的“幕后”工作,这将更容易理解:How do servlets work? Instantiation, sessions, shared variables and multithreading。


@Request/View/Flow/Session/ApplicationScoped

@RequestScoped bean 的生命周期与单个 HTTP 请求-响应周期一样长(请注意,Ajax 请求也算作单个 HTTP 请求)。只要您通过回发调用返回null/void 而没有任何导航/重定向的操作方法,@ViewScoped bean 就会存在。只要您在流配置文件中注册的指定视图集合中导航,@FlowScoped bean 就会存在。 @SessionScoped bean 与已建立的 HTTP 会话一样长。只要 Web 应用程序运行,@ApplicationScoped bean 就会存在。请注意,CDI @Model 基本上是 stereotype 对应于 @Named @RequestScoped,因此适用相同的规则。

选择哪个范围完全取决于 bean 持有和表示的数据(状态)。使用 @RequestScoped 进行简单和非 ajax 表单/演示。将@ViewScoped 用于启用了丰富 ajax 的动态视图(基于 ajax 的验证、呈现、对话框等)。将@FlowScoped 用于收集分布在多个页面上的输入数据的“向导”(“问卷”)模式。将@SessionScoped 用于客户端特定数据,例如登录用户和用户首选项(语言等)。将@ApplicationScoped 用于应用程序范围的数据/常量,例如每个人都相同的下拉列表,或者没有任何实例变量且只有方法的托管 bean。

为会话/视图/请求范围的数据滥用@ApplicationScoped bean 会使其在所有用户之间共享,因此其他任何人都可以看到彼此的数据,这是完全错误的。为视图/请求范围的数据滥用@SessionScoped bean 将使其在单个浏览器会话中的所有选项卡/窗口之间共享,因此最终用户在选项卡之间切换后与每个视图交互时可能会遇到不一致性,这对用户体验不利.对视图范围数据滥用@RequestScoped bean 会使视图范围数据在每个(ajax)回发时重新初始化为默认值,从而可能导致表单无法工作(see also points 4 and 5 here)。将@ViewScoped bean 用于请求、会话或应用程序范围的数据,以及将@SessionScoped bean 用于应用程序范围的数据不会影响客户端,但它会不必要地占用服务器内存并且效率很低。

请注意,不应根据性能影响来选择范围,除非您真的内存占用量很小并且希望完全无状态;您需要专门使用 @RequestScoped bean 并使用请求参数来维护客户端的状态。另请注意,当您有一个具有不同范围数据的 JSF 页面时,将它们放在与数据范围匹配的范围内的单独支持 bean 中是完全有效的。对于 JSF 托管 bean,这些 bean 可以通过 @ManagedProperty 相互访问,对于 CDI 托管 bean,则可以通过 @Inject 相互访问。

另见:

Difference between View and Request scope in managed beans Advantages of using JSF Faces Flow instead of the normal navigation system Communication in JSF2 - Managed bean scopes

@CustomScoped/NoneScoped/Dependent

您的问题中没有提到,但(旧版)JSF 还支持 @CustomScoped@NoneScoped,这在现实世界中很少使用。 @CustomScoped 必须在更广泛的范围内引用自定义 Map<K, Bean> 实现,该实现已覆盖 Map#put() 和/或 Map#get(),以便对 bean 创建和/或销毁进行更细粒度的控制。

JSF @NoneScoped 和 CDI @Dependent 基本上与 bean 上的单个 EL 评估一样长。想象一个登录表单,其中有两个输入字段引用一个 bean 属性和一个命令按钮引用一个 bean 动作,因此总共有三个 EL 表达式,那么将有效地创建三个实例。一个设置了用户名,一个设置了密码,一个设置了操作。您通常只想在 bean 上使用此范围,这些 bean 应该与注入它的 bean 一样长。因此,如果将 @NoneScoped@Dependent 注入到 @SessionScoped 中,那么它将与 @SessionScoped bean 一样长。

另见:

Expire specific managed bean instance after time interval what is none scope bean and when to use it? What is the default Managed Bean Scope in a JSF 2 application?

闪光范围

最后,JSF 也支持 flash 作用域。它由一个短暂的 cookie 支持,该 cookie 与会话范围内的数据条目相关联。在重定向之前,将在 HTTP 响应上设置一个 cookie,其值与会话范围内的数据条目唯一关联。重定向后,将检查 flash 范围 cookie 的存在,并将与 cookie 关联的数据条目从会话范围中删除,并放入重定向请求的请求范围中。最后,cookie 将从 HTTP 响应中删除。这样,重定向的请求就可以访问在初始请求中准备的请求范围数据。

这实际上不能用作托管 bean 范围,即没有 @FlashScoped 这样的东西。 flash 范围只能通过托管 bean 中的 ExternalContext#getFlash() 和 EL 中的 #flash 作为映射使用。

另见:

How to show faces message in the redirected page Pass an object between @ViewScoped beans without using GET params CDI missing @ViewScoped and @FlashScoped

【讨论】:

我认为 your answer 对“视图范围 bean 在 JSF 中如何以及何时销毁?”问题的引用与此处相关。 @Cold:这是一个旧的 CDI 范围,在 JSF 2.2 中替换为 @FlowScoped(无需手动启动/停止它)。 而DeltaSpike另外还有ViewAccesscopedWindowScoped @BalusC,我认为 MyFaces 2.2 中的 ViewScoped bean 存在问题。我目前面临ViewScoped bean 和Ajax 的问题,我已发布here。在 MyFaces JIRA 中,也有一个 discussion 讨论这个话题。 CDI 定义了四个内置作用域:@RequestScoped@SessionScoped@ApplicationScoped@ConversationScoped为什么你描述的作用域不同?【参考方案2】:

自 JSF 2.3 起,包javax.faces.bean 包中定义的所有 bean 范围都已被弃用,以使范围与 CDI 保持一致。此外,它们仅适用于您的 bean 使用 @ManagedBean 注释的情况。如果您使用的是 2.3 以下的 JSF 版本,请参阅最后的旧版答案。


从 JSF 2.3 开始,这里是可用于 JSF 支持 Bean 的范围:

1. @javax.enterprise.context.ApplicationScoped:应用程序范围在 Web 应用程序的整个持续时间内持续存在。该范围在所有请求和所有会话之间共享。当您拥有整个应用程序的数据时,这很有用。

2。 @javax.enterprise.context.SessionScoped:从会话建立到会话终止,会话范围一直存在。会话上下文在同一 HTTP 会话中发生的所有请求之间共享。当您不想为特定会话保存特定客户端的数据时,这很有用。

3. @javax.enterprise.context.ConversationScoped:会话范围与 bean 一样存在于日志中。作用域提供 2 种方法:Conversation.begin()Conversation.end()。这些方法应该显式调用,以开始或结束 bean 的生命。

4. @javax.enterprise.context.RequestScoped:请求范围是短暂的。它在提交 HTTP 请求时开始,在响应发送回客户端后结束。如果将托管 bean 放入请求范围,则每个请求都会创建一个新实例。如果您担心会话范围存储的成本,则值得考虑请求范围。

5. @javax.faces.flow.FlowScoped:只要 Flow 存在,Flow 范围就会一直存在。流可以定义为定义工作单元的一组包含的页面(或视图)。只要用户在 Flow 中导航,Flow 作用域就处于活动状态。

6. @javax.faces.view.ViewScoped:在重新显示相同的 JSF 页面时,视图范围内的 bean 仍然存在。一旦用户导航到不同的页面,bean 就会超出范围。


以下旧答案适用于 2.3 之前的 JSF 版本

从 JSF 2.x 开始,有 4 个 Bean 作用域:

@SessionScoped @RequestScoped @ApplicationScoped @ViewScoped

会话范围:会话范围从会话建立到会话终止持续存在。会话终止 如果 Web 应用程序在 HttpSession 对象,或者如果它超时。

RequestScope:请求范围是短暂的。它在提交 HTTP 请求时开始,在返回响应后结束 给客户。如果将托管 bean 放入请求范围,则新的 每个请求都会创建实例。值得考虑的请求 如果您担心会话范围存储的成本,请使用范围。

ApplicationScope:应用程序范围在 Web 应用程序的整个持续时间内持续存在。该范围在所有人之间共享 请求和所有会话。您将托管 bean 放入 应用程序范围,如果应该在所有 bean 之间共享单个 bean Web 应用程序的实例。 bean 是在它被构建的时候构建的 首先由应用程序的任何用户请求,并且它保持活动状态 直到 Web 应用程序从应用程序服务器中删除。

ViewScope:视图范围是在 JSF 2.0 中添加的。在重新显示相同的 JSF 页面时,视图范围内的 bean 仍然存在。 (JSF 规范使用术语视图来表示 JSF 页面。)只要用户 导航到不同的页面,bean 超出范围。

根据您的要求选择您的范围。

来源: Core Java Server Faces 3rd Edition by David Geary 和 Cay Horstmann [页号。 51 - 54]

【讨论】:

您能否澄清一下,“HttpSession 对象上的无效方法”是什么意思:invalidate() 方法,还是无效方法? 有点老,可能回复晚了,但澄清一下:FacesContext.getCurrentInstance().getExternalContext().invalidateSession(); 在你的“注销 bean”中被调用就是他的意思。 它成为遗留答案,目前有 8 个作用域 @KishorPrakash:现在是 6 个月前。 ;-) @Kukeltje:对不起,我正在处理。

以上是关于如何选择合适的bean范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据不同企业工况选择合适的液压扳手

如何选择一款合适的密码管理器(转载自ImportNew)

MYSQL中如何选择合适的数据类型

如何在OpenCV中为InRange阈值选择颜色的最佳HSV值

大厂十年IT老兵血泪教训,IT人到底要如何选择合适的报表工具?

Javascript DOM如何隐藏不合适的复选框