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

@RequestScoped
@ViewScoped
@FlowScoped
@SessionScoped
@ApplicationScoped

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


简介

它表示bean的范围(生命周期)。如果您熟悉基本servlet web应用程序的“幕后工作”,这就更容易理解:servlet是如何工作的?实例化、会话、共享变量和多线程。


@Request /视图/流/会议/ ApplicationScoped

A @RequestScoped bean lives as long as a single HTTP request-response cycle (note that an Ajax request counts as a single HTTP request too). A @ViewScoped bean lives as long as you're interacting with the same JSF view by postbacks which call action methods returning null/void without any navigation/redirect. A @FlowScoped bean lives as long as you're navigating through the specified collection of views registered in the flow configuration file. A @SessionScoped bean lives as long as the established HTTP session. An @ApplicationScoped bean lives as long as the web application runs. Note that the CDI @Model is basically a stereotype for @Named @RequestScoped, so same rules apply.

Which scope to choose depends solely on the data (the state) the bean holds and represents. Use @RequestScoped for simple and non-ajax forms/presentations. Use @ViewScoped for rich ajax-enabled dynamic views (ajaxbased validation, rendering, dialogs, etc). Use @FlowScoped for the "wizard" ("questionnaire") pattern of collecting input data spread over multiple pages. Use @SessionScoped for client specific data, such as the logged-in user and user preferences (language, etc). Use @ApplicationScoped for application wide data/constants, such as dropdown lists which are the same for everyone, or managed beans without any instance variables and having only methods.

Abusing an @ApplicationScoped bean for session/view/request scoped data would make it to be shared among all users, so anyone else can see each other's data which is just plain wrong. Abusing a @SessionScoped bean for view/request scoped data would make it to be shared among all tabs/windows in a single browser session, so the enduser may experience inconsitenties when interacting with every view after switching between tabs which is bad for user experience. Abusing a @RequestScoped bean for view scoped data would make view scoped data to be reinitialized to default on every single (ajax) postback, causing possibly non-working forms (see also points 4 and 5 here). Abusing a @ViewScoped bean for request, session or application scoped data, and abusing a @SessionScoped bean for application scoped data doesn't affect the client, but it unnecessarily occupies server memory and is plain inefficient.

Note that the scope should rather not be chosen based on performance implications, unless you really have a low memory footprint and want to go completely stateless; you'd need to use exclusively @RequestScoped beans and fiddle with request parameters to maintain the client's state. Also note that when you have a single JSF page with differently scoped data, then it's perfectly valid to put them in separate backing beans in a scope matching the data's scope. The beans can just access each other via @ManagedProperty in case of JSF managed beans or @Inject in case of CDI managed beans.

参见:

托管bean中的视图范围和请求范围之间的差异 使用JSF Faces Flow而不是普通导航系统的优点 JSF2 -托管bean作用域中的通信


@CustomScoped / NoneScoped /依赖

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

The JSF @NoneScoped and CDI @Dependent basically lives as long as a single EL-evaluation on the bean. Imagine a login form with two input fields referring a bean property and a command button referring a bean action, thus with in total three EL expressions, then effectively three instances will be created. One with the username set, one with the password set and one on which the action is invoked. You normally want to use this scope only on beans which should live as long as the bean where it's being injected. So if a @NoneScoped or @Dependent is injected in a @SessionScoped, then it will live as long as the @SessionScoped bean.

参见:

特定的托管bean实例在时间间隔后过期 什么是无范围bean,什么时候使用它? JSF 2应用程序中默认的托管Bean作用域是什么?


Flash范围

As last, JSF also supports the flash scope. It is backed by a short living cookie which is associated with a data entry in the session scope. Before the redirect, a cookie will be set on the HTTP response with a value which is uniquely associated with the data entry in the session scope. After the redirect, the presence of the flash scope cookie will be checked and the data entry associated with the cookie will be removed from the session scope and be put in the request scope of the redirected request. Finally the cookie will be removed from the HTTP response. This way the redirected request has access to request scoped data which was been prepared in the initial request.

这实际上是无法作为托管bean作用域使用的,也就是说,没有@FlashScoped这样的东西。flash作用域只能通过托管bean中的ExternalContext#getFlash()和EL中的#{flash}作为映射来使用。

参见:

如何在重定向页面中显示人脸消息 在@ViewScoped bean之间传递一个对象而不使用GET参数 CDI缺少@ViewScoped和@FlashScoped


自JSF 2.3以来,所有在包javax.faces.bean包中定义的bean作用域都已弃用,以使作用域与CDI保持一致。此外,它们只适用于bean使用@ManagedBean注释的情况。如果您使用的是2.3以下的JSF版本,请参考最后的遗留答案。


从JSF 2.3开始,这里有一些可以在JSF Backing Beans上使用的作用域:

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

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

3.@javax.enterprise.context。ConversationScoped:会话范围在bean存在时一直存在。作用域提供了两个方法:Conversation.begin()和Conversation.end()。应该显式地调用这些方法,以开始或结束bean的生命周期。

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

5. @javax.faces.flow。FlowScoped:只要流存在,流范围就会持续存在。流可以定义为定义工作单元的包含页面(或视图)集。只要用户在流中导航,流范围是活动的。

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


下面的遗留答案适用于2.3之前的JSF版本

As of JSF 2.x there are 4 Bean Scopes: @SessionScoped @RequestScoped @ApplicationScoped @ViewScoped Session Scope: The session scope persists from the time that a session is established until session termination. A session terminates if the web application invokes the invalidate method on the HttpSession object, or if it times out. RequestScope: The request scope is short-lived. It starts when an HTTP request is submitted and ends after the response is sent back to the client. If you place a managed bean into request scope, a new instance is created with each request. It is worth considering request scope if you are concerned about the cost of session scope storage. ApplicationScope: The application scope persists for the entire duration of the web application. That scope is shared among all requests and all sessions. You place managed beans into the application scope if a single bean should be shared among all instances of a web application. The bean is constructed when it is first requested by any user of the application, and it stays alive until the web application is removed from the application server. ViewScope: View scope was added in JSF 2.0. A bean in view scope persists while the same JSF page is redisplayed. (The JSF specification uses the term view for a JSF page.) As soon as the user navigates to a different page, the bean goes out of scope. Choose the scope you based on your requirement.

来源:Core Java Server Faces第三版,作者:David Geary & Cay Horstmann51 - 54]