SeamFramework.orgCommunity Documentation

第 5 章 范围和上下文

5.1. 范围类型
5.2. 内置范围
5.3. 对话范围
5.3.1. 对话划分
5.3.2. 对话的传播
5.3.3. 对话超时
5.4. The singleton pseudo-scope
5.5. 依赖的伪范围
5.6. The @New qualifier

So far, we've seen a few examples of scope type annotations. The scope of a bean determines the lifecycle of instances of the bean. The scope also determines which clients refer to which instances of the bean. According to the CDI specification, a scope determines:

  • When a new instance of any bean with that scope is created

  • When an existing instance of any bean with that scope is destroyed

  • Which injected references refer to any instance of a bean with that scope

For example, if we have a session-scoped bean, CurrentUser, all beans that are called in the context of the same HttpSession will see the same instance of CurrentUser. This instance will be automatically created the first time a CurrentUser is needed in that session, and automatically destroyed when the session ends.

注意

JPA entities aren't a great fit for this model. Entities have their whole own lifecycle and identity model which just doesn't map naturally to the model used in CDI. Therefore, we recommend against treating entities as CDI beans. You're certainly going to run into problems if you try to give an entity a scope other than the default scope @Dependent. The client proxy will get in the way if you try to pass an injected instance to the JPA EntityManager.

CDI features an extensible context model. It's possible to define new scopes by creating a new scope type annotation:

@ScopeType

@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface ClusterScoped {}

Of course, that's the easy part of the job. For this scope type to be useful, we will also need to define a Context object that implements the scope! Implementing a Context is usually a very technical task, intended for framework development only. You can expect an implementation of the business scope, for instance, in a future version of Seam.

We can apply a scope type annotation to a bean implementation class to specify the scope of the bean:

@ClusterScoped

public class SecondLevelCache { ... }

Usually, you'll use one of CDI's built-in scopes.

CDI defines four built-in scopes:

For a web application that uses CDI:

A CDI extension can implement support for the conversation scope in other web frameworks.

在下列情况下请求和应用范围是激活的:

If the application tries to invoke a bean with a scope that does not have an active context, a ContextNotActiveException is thrown by the container at runtime.

Managed beans with scope @SessionScoped or @ConversationScoped must be serializable, since the container passivates the HTTP session from time to time.

Three of the four built-in scopes should be extremely familiar to every Java EE developer, so let's not waste time discussing them here. One of the scopes, however, is new.

The conversation scope is a bit like the traditional session scope in that it holds state associated with a user of the system, and spans multiple requests to the server. However, unlike the session scope, the conversation scope:

A conversation represents a task—a unit of work from the point of view of the user. The conversation context holds state associated with what the user is currently working on. If the user is doing multiple things at the same time, there are multiple conversations.

The conversation context is active during any JSF request. Most conversations are destroyed at the end of the request. If a conversation should hold state across multiple requests, it must be explicitly promoted to a long-running conversation.

In addition to the four built-in scopes, CDI also supports two pseudo-scopes. The first is the singleton pseudo-scope, which we specify using the annotation @Singleton.

You can guess what "singleton" means here. It means a bean that is instantiated once. Unfortunately, there's a little problem with this pseudo-scope. Beans with scope @Singleton don't have a proxy object. Clients hold a direct reference to the singleton instance. So we need to consider the case of a client that can be serialized, for example, any bean with scope @SessionScoped or @ConversationScoped, any dependent object of a bean with scope @SessionScoped or @ConversationScoped, or any stateful session bean.

Now, if the singleton instance is a simple, immutable, serializable object like a string, a number or a date, we probably don't mind too much if it gets duplicated via serialization. However, that makes it no stop being a true singleton, and we may as well have just declared it with the default scope.

There are several ways to ensure that the singleton bean remains a singleton when its client gets serialized:

A fourth, better solution is to instead use @ApplicationScoped, allowing the container to proxy the bean, and take care of serialization problems automatically.

Finally, CDI features the so-called dependent pseudo-scope. This is the default scope for a bean which does not explicitly declare a scope type.

For example, this bean has the scope type @Dependent:

public class Calculator { ... }

An instance of a dependent bean is never shared between different clients or different injection points. It is strictly a dependent object of some other object. It is instantiated when the object it belongs to is created, and destroyed when the object it belongs to is destroyed.

If a Unified EL expression refers to a dependent bean by EL name, an instance of the bean is instantiated every time the expression is evaluated. The instance is not reused during any other expression evaluation.

Beans with scope @Dependent don't need a proxy object. The client holds a direct reference to its instance.

CDI makes it easy to obtain a dependent instance of a bean, even if the bean is already declared as a bean with some other scope type.

The built-in qualifier @New allows us to obtain a dependent object of a specified class.

@Inject @New Calculator calculator;

The class must be a valid managed bean or session bean, but need not be an enabled bean.

This works even if Calculator is already declared with a different scope type, for example:

@ConversationScoped

public class Calculator { ... }

所以下面注入的属性,每个都获得一个不同的 Calculator实例:

public class PaymentCalc {

   @Inject Calculator calculator;
   @Inject @New Calculator newCalculator;
}

calculator域有一个对话范围的Calculator实例注入。newCalculator域有一个新的Calculator实例注入,这个实例的生命周期绑定在其拥有者PaymentCalc类上。

This feature is particularly useful with producer methods, as we'll see in 第 8 章 生产者方法.