Bean Scopes

1.5. Bean Scopes

When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.

当您创建一个bean定义时,您创建了一个配方,用于创建由该bean定义定义的类的实际实例。bean定义是一个菜谱的想法很重要,因为这意味着,与使用类一样,您可以从一个菜谱创建许多对象实例。

You can control not only the various dependencies and configuration values that are to be plugged into an object that is created from a particular bean definition but also control the scope of the objects created from a particular bean definition. This approach is powerful and flexible, because you can choose the scope of the objects you create through configuration instead of having to bake in the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes. The Spring Framework supports six scopes, four of which are available only if you use a web-aware ApplicationContext. You can also create a custom scope.

您不仅可以控制插入到从特定bean定义创建的对象中的各种依赖项和配置值,还可以控制从该对象创建的对象的范围。特定的bean定义。这种方法功能强大且灵活,因为您可以通过配置选择创建的对象的范围,而不必在Java类级别固定对象的范围。可以将bean定义为部署在多个作用域中的一个。Spring框架支持6个作用域,其中4个只有在使用web感知的ApplicationContext时才可用。还可以创建自定义范围。

The following table describes the supported scopes:

支持的作用域如下表所示:
Scope Description 说明
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. (默认)将每个Spring IoC容器的单个bean定义定义为单个对象实例。
prototype Scopes a single bean definition to any number of object instances. 将单个bean定义作用于任意数量的对象实例。
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. 将单个bean定义作用于单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的基于单个bean定义创建的bean实例。只在web感知Spring ApplicationContext的上下文中有效。
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. 将单个bean定义作用于HTTP会话的生命周期。只在web感知Spring ApplicationContext的上下文中有效。
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. 将单个bean定义作用于ServletContext的生命周期。只在web感知Spring ApplicationContext的上下文中有效。
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. 将单个bean定义作用于WebSocket的生命周期。只在web感知Spring ApplicationContext的上下文中有效。

As of Spring 3.0, a thread scope is available but is not registered by default. For more information, see the documentation for SimpleThreadScope. For instructions on how to register this or any other custom scope, see Using a Custom Scope.

从Spring 3.0开始,线程作用域是可用的,但默认情况下没有注册。有关更多信息,请参阅SimpleThreadScope的文档。有关如何注册此或任何其他自定义作用域的说明,请参阅使用自定义作用域。

1.5.1. The Singleton Scope

Only one shared instance of a singleton bean is managed, and all requests for beans with an ID or IDs that match that bean definition result in that one specific bean instance being returned by the Spring container.

只管理一个单例bean的一个共享实例,所有对具有一个或多个ID的bean的请求都将导致Spring容器返回一个特定的bean实例。

To put it another way, when you define a bean definition and it is scoped as a singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object. The following image shows how the singleton scope works:

换句话说,当您定义一个bean定义,并且它的作用域为单例时,Spring IoC容器恰好创建该bean定义所定义的对象的一个实例。此单一实例存储在此类单例bean的缓存中,对该命名bean的所有后续请求和引用将返回缓存的对象。下图展示了单例作用域是如何工作的:

singleton

Spring’s concept of a singleton bean differs from the singleton pattern as defined in the Gang of Four (GoF) patterns book. The GoF singleton hard-codes the scope of an object such that one and only one instance of a particular class is created per ClassLoader. The scope of the Spring singleton is best described as being per-container and per-bean. This means that, if you define one bean for a particular class in a single Spring container, the Spring container creates one and only one instance of the class defined by that bean definition. The singleton scope is the default scope in Spring. To define a bean as a singleton in XML, you can define a bean as shown in the following example:

Spring的单例bean概念不同于四人组(GoF)模式书中定义的单例模式。GoF单例硬编码对象的作用域,使得每个ClassLoader只创建一个特定类的实例。Spring单例的作用域最好描述为每个容器和每个bean。这意味着,如果您为单个Spring容器中的特定类定义了一个bean,那么Spring容器将创建一个且仅一个由该bean定义的类的实例。单例作用域是Spring中的默认作用域。要在XML中将bean定义为单例,您可以如下所示定义一个bean:
<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

1.5.2. The Prototype Scope

The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean() method call on the container. As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

bean部署的非单例原型范围会导致每次对特定bean发出请求时创建一个新的bean实例。也就是说,该bean被注入到另一个bean中,或者您通过容器上的getBean()方法调用请求它。作为一条规则,您应该对所有有状态bean使用原型作用域,对无状态bean使用单例作用域。

The following diagram illustrates the Spring prototype scope:

下图说明了Spring原型的作用域:

image

(A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state. It was easier for us to reuse the core of the singleton diagram.)

数据访问对象(DAO)通常不会配置为原型,因为典型的DAO不包含任何会话状态。我们更容易重用单例图的核心。)

The following example defines a bean as a prototype in XML:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype beans hold. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.

与其他作用域相比,Spring并不管理原型bean的完整生命周期。容器实例化、配置或以其他方式组装一个原型对象并将其交给客户端,而不需要该原型实例的进一步记录。因此,尽管初始化生命周期回调方法在所有对象上都被调用,而不考虑作用域,但在原型的情况下,配置的销毁生命周期回调不会被调用。客户机代码必须清理原型作用域的对象,并释放原型bean持有的昂贵资源。要让Spring容器释放由原型作用域bean持有的资源,请尝试使用自定义bean后处理器,它持有对需要清理的bean的引用。

In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client. (For details on the lifecycle of a bean in the Spring container, see Lifecycle Callbacks.)

在某些方面,Spring容器对于原型作用域bean的作用是Java new操作符的替代品。超过该点的所有生命周期管理都必须由客户机处理。(关于Spring容器中bean生命周期的详细信息,请参见生命周期回调。)

1.5.3. Singleton Beans with Prototype-bean Dependencies

When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus, if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.

当您使用具有依赖原型bean的单例作用域bean时,请注意依赖关系在实例化时被解析。因此,如果您将依赖注入原型作用域的bean到单例作用域的bean中,则会实例化一个新的原型bean,然后将依赖注入到单例bean中。原型实例是曾经提供给单例作用域bean的唯一实例。

However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container instantiates the singleton bean and resolves and injects its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Method Injection.

但是,假设您希望单例作用域bean在运行时反复获得原型作用域bean的新实例。您不能将依赖注入原型作用域的bean到您的单例bean中,因为这种注入只发生一次,即Spring容器实例化单例bean并解析并注入它的依赖项时。如果您在运行时多次需要一个原型bean的新实例,请参阅方法注入。

1.5.4. Request, Session, Application, and WebSocket Scopes

The request, session, application, and websocket scopes are available only if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you use these scopes with regular Spring IoC containers, such as the ClassPathXmlApplicationContext, an IllegalStateException that complains about an unknown bean scope is thrown.

request、session、application和websocket作用域只有在使用web感知Spring ApplicationContext实现(如XmlWebApplicationContext)时才可用。如果在常规Spring IoC容器(如ClassPathXmlApplicationContext)中使用这些作用域,则会抛出一个关于未知bean作用域的IllegalStateException。
Initial Web Configuration

To support the scoping of beans at the request, session, application, and websocket levels (web-scoped beans), some minor initial configuration is required before you define your beans. (This initial setup is not required for the standard scopes: singleton and prototype.)

为了支持bean在request、session、application和websocket级别(web范围的bean)的作用域,在定义bean之前需要进行一些次要的初始配置。(对于标准作用域:singleton和prototype,这个初始设置是不需要的。)

How you accomplish this initial setup depends on your particular Servlet environment.

如何完成这个初始设置取决于特定的Servlet环境。

If you access scoped beans within Spring Web MVC, in effect, within a request that is processed by the Spring DispatcherServlet, no special setup is necessary. DispatcherServlet already exposes all relevant state.

实际上,如果您在Spring Web MVC中访问有作用域的bean,在Spring DispatcherServlet处理的请求中,不需要特殊的设置。DispatcherServlet已经公开了所有相关的状态。

If you use a Servlet 2.5 web container, with requests processed outside of Spring’s DispatcherServlet (for example, when using JSF or Struts), you need to register the org.springframework.web.context.request.RequestContextListener ServletRequestListener. For Servlet 3.0+, this can be done programmatically by using the WebApplicationInitializer interface. Alternatively, or for older containers, add the following declaration to your web application’s web.xml file:

如果你使用一个Servlet 2.5 web容器,请求在Spring的DispatcherServlet之外处理(例如,当使用JSF或Struts时),你需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于Servlet 3.0+,这可以通过使用WebApplicationInitializer接口以编程的方式完成。或者,对于旧的容器,添加以下声明到您的web应用程序的web.xml文件:
<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

Alternatively, if there are issues with your listener setup, consider using Spring’s RequestContextFilter. The filter mapping depends on the surrounding web application configuration, so you have to change it as appropriate. The following listing shows the filter part of a web application:

或者,如果你的监听器设置有问题,考虑使用Spring的' RequestContextFilter '。过滤器映射取决于周围的web应用程序配置,因此您必须适当地更改它。下面的列表显示了一个web应用程序的过滤器部分:
<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

DispatcherServlet, RequestContextListener, and RequestContextFilter all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.

DispatcherServlet、RequestContextListener和RequestContextFilter都做完全相同的事情,即将HTTP请求对象绑定到服务该请求的线程。这使得请求作用域和会话作用域的bean在调用链的更下游可用。
Request scope

Consider the following XML configuration for a bean definition:

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

The Spring container creates a new instance of the LoginAction bean by using the loginAction bean definition for each and every HTTP request. That is, the loginAction bean is scoped at the HTTP request level. You can change the internal state of the instance that is created as much as you want, because other instances created from the same loginAction bean definition do not see these changes in state. They are particular to an individual request. When the request completes processing, the bean that is scoped to the request is discarded.

Spring容器通过为每个HTTP请求使用LoginAction bean定义来创建LoginAction bean的新实例。也就是说,loginAction bean的作用域在HTTP请求级别。您可以随心所欲地更改创建的实例的内部状态,因为从相同loginAction bean定义创建的其他实例在状态中看不到这些更改。它们是针对个别要求的。当请求完成处理时,将丢弃作用域为该请求的bean。

When using annotation-driven components or Java configuration, the @RequestScope annotation can be used to assign a component to the request scope. The following example shows how to do so:

当使用注释驱动的组件或Java配置时,可以使用@RequestScope注释将组件分配给请求范围。下面的例子展示了如何这样做:
@RequestScope
@Component
public class LoginAction {
    // ...
}
Session Scope

Consider the following XML configuration for a bean definition:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

The Spring container creates a new instance of the UserPreferences bean by using the userPreferences bean definition for the lifetime of a single HTTP Session. In other words, the userPreferences bean is effectively scoped at the HTTP Session level. As with request-scoped beans, you can change the internal state of the instance that is created as much as you want, knowing that other HTTP Session instances that are also using instances created from the same userPreferences bean definition do not see these changes in state, because they are particular to an individual HTTP Session. When the HTTP Session is eventually discarded, the bean that is scoped to that particular HTTP Session is also discarded.

Spring容器通过在单个HTTP会话的生命周期中使用UserPreferences bean定义来创建UserPreferences bean的新实例。换句话说,userPreferences bean的作用域有效地限定在HTTP Session级别。与使用请求范围的bean一样,您可以随心所欲地更改创建的实例的内部状态,因为其他HTTP Session实例也使用从相同userPreferences bean定义创建的实例,它们不会看到这些状态变化,因为它们是特定于单个HTTP Session的。当HTTP会话最终被丢弃时,作用域为该特定HTTP会话的bean也被丢弃。

When using annotation-driven components or Java configuration, you can use the @SessionScope annotation to assign a component to the session scope.

当使用注释驱动的组件或Java配置时,您可以使用@SessionScope注释将组件分配给会话作用域。
@SessionScope
@Component
public class UserPreferences {
    // ...
}
Application Scope

Consider the following XML configuration for a bean definition:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

The Spring container creates a new instance of the AppPreferences bean by using the appPreferences bean definition once for the entire web application. That is, the appPreferences bean is scoped at the ServletContext level and stored as a regular ServletContext attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per ServletContext, not per Spring ApplicationContext (for which there may be several in any given web application), and it is actually exposed and therefore visible as a ServletContext attribute.

通过对整个web应用程序使用一次AppPreferences bean定义,Spring容器创建了一个AppPreferences bean的新实例。也就是说,appPreferences bean的作用域在ServletContext级别,并存储为常规的ServletContext属性。这有点类似于Spring的单例bean,但在两个重要的方面有所不同:它是每个ServletContext的单例,而不是每个Spring ApplicationContext(在任何给定的web应用程序中可能有几个),它实际上是公开的,因此作为ServletContext属性可见。

When using annotation-driven components or Java configuration, you can use the @ApplicationScope annotation to assign a component to the application scope. The following example shows how to do so:

当使用注释驱动的组件或Java配置时,您可以使用@ApplicationScope注释将组件分配给应用程序作用域。下面的例子展示了如何这样做:
@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
WebSocket Scope

WebSocket scope is associated with the lifecycle of a WebSocket session and applies to STOMP over WebSocket applications, see WebSocket scope for more details.

WebSocket作用域与WebSocket会话的生命周期相关联,并应用于STOMP over WebSocket应用程序,详见WebSocket作用域。
Scoped Beans as Dependencies

The Spring IoC container manages not only the instantiation of your objects (beans), but also the wiring up of collaborators (or dependencies). If you want to inject (for example) an HTTP request-scoped bean into another bean of a longer-lived scope, you may choose to inject an AOP proxy in place of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real target object from the relevant scope (such as an HTTP request) and delegate method calls onto the real object.

Spring IoC容器不仅管理对象(bean)的实例化,而且还管理协作者(或依赖项)的连接。如果您想(例如)将一个HTTP请求作用域的bean注入到另一个更长的作用域的bean中,您可以选择注入一个AOP代理来代替作用域的bean。也就是说,您需要注入一个代理对象,该对象公开与作用域对象相同的公共接口,但也可以从相关作用域(例如HTTP请求)检索真正的目标对象,并将方法调用委托给真正的对象。

You may also use <aop:scoped-proxy/> between beans that are scoped as singleton, with the reference then going through an intermediate proxy that is serializable and therefore able to re-obtain the target singleton bean on deserialization.

When declaring <aop:scoped-proxy/> against a bean of scope prototype, every method call on the shared proxy leads to the creation of a new target instance to which the call is then being forwarded.

Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also declare your injection point (that is, the constructor or setter argument or autowired field) as ObjectFactory<MyTargetBean>, allowing for a getObject() call to retrieve the current instance on demand every time it is needed — without holding on to the instance or storing it separately.

As an extended variant, you may declare ObjectProvider<MyTargetBean> which delivers several additional access variants, including getIfAvailable and getIfUnique.

The JSR-330 variant of this is called Provider and is used with a Provider<MyTargetBean> declaration and a corresponding get() call for every retrieval attempt. See here for more details on JSR-330 overall.

您还可以在作用域为单例的bean之间使用<aop:scoped-proxy/>,然后引用通过一个可序列化的中间代理,因此能够在反序列化时重新获得目标单例bean。

当针对范围原型bean声明<aop:scoped-proxy/>时,共享代理上的每个方法调用都会导致创建一个新的目标实例,然后将调用转发到该目标实例。

而且,限定作用域的代理并不是以生命周期安全的方式从更短的作用域访问bean的唯一方法。您还可以将注入点(即构造函数或setter参数或自动连接字段)声明为ObjectFactory<MyTargetBean>,从而允许getObject()调用在每次需要当前实例时根据需要检索当前实例—而无需保留实例或单独存储它。

作为一个扩展变量,您可以声明ObjectProvider<MyTargetBean>,它提供了几个额外的访问变量,包括getIfAvailable和getIfUnique。

此功能的JSR-330变体称为Provider,并与Provider<MyTargetBean>声明和对应的get()调用一起用于每次检索尝试。请参阅这里了解更多关于JSR-330的详细信息。

The configuration in the following example is only one line, but it is important to understand the “why” as well as the “how” behind it:

下面例子中的配置只有一行,但是理解它背后的“为什么”和“如何”是很重要的:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> 
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>
  1. The line that defines the proxy.

To create such a proxy, you insert a child <aop:scoped-proxy/> element into a scoped bean definition (see Choosing the Type of Proxy to Create and XML Schema-based configuration). Why do definitions of beans scoped at the request, session and custom-scope levels require the <aop:scoped-proxy/> element? Consider the following singleton bean definition and contrast it with what you need to define for the aforementioned scopes (note that the following userPreferences bean definition as it stands is incomplete):

要创建这样的代理,您需要将子<aop:scoped-proxy/>元素插入到有范围的bean定义中(请参阅选择要创建的代理类型和基于XML模式的配置)。为什么在请求、会话和自定义作用域级别定义bean需要<aop:scoped-proxy/>元素?考虑下面的单例bean定义,并将其与您需要为前面提到的作用域定义的内容进行对比(注意,下面的userPreferences bean定义目前是不完整的):
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

In the preceding example, the singleton bean (userManager) is injected with a reference to the HTTP Session-scoped bean (userPreferences). The salient point here is that the userManager bean is a singleton: it is instantiated exactly once per container, and its dependencies (in this case only one, the userPreferences bean) are also injected only once. This means that the userManager bean operates only on the exact same userPreferences object (that is, the one with which it was originally injected).

在前面的示例中,单例bean (userManager)被注入了对HTTP会话作用域bean (userPreferences)的引用。这里突出的一点是userManager bean是一个单例:它在每个容器中只实例化一次,并且它的依赖项(在本例中只有一个,userPreferences bean)也只注入一次。这意味着userManager bean只操作完全相同的userPreferences对象(即,它最初被注入的对象)。

This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean (for example, injecting an HTTP Session-scoped collaborating bean as a dependency into singleton bean). Rather, you need a single userManager object, and, for the lifetime of an HTTP Session, you need a userPreferences object that is specific to the HTTP Session. Thus, the container creates an object that exposes the exact same public interface as the UserPreferences class (ideally an object that is a UserPreferences instance), which can fetch the real UserPreferences object from the scoping mechanism (HTTP request, Session, and so forth). The container injects this proxy object into the userManager bean, which is unaware that this UserPreferences reference is a proxy. In this example, when a UserManager instance invokes a method on the dependency-injected UserPreferences object, it is actually invoking a method on the proxy. The proxy then fetches the real UserPreferences object from (in this case) the HTTP Session and delegates the method invocation onto the retrieved real UserPreferences object.

当将较短作用域的bean注入到较长作用域的bean中时,这不是您想要的行为(例如,将HTTP session作用域的协作bean作为依赖注入到单例bean中)。相反,您需要一个单一的userManager对象,并且在HTTP会话的生命周期内,您需要一个特定于HTTP会话的userPreferences对象。因此,容器创建了一个对象,该对象公开了与UserPreferences类完全相同的公共接口(理想情况下是一个UserPreferences实例对象),它可以从作用域机制(HTTP请求、Session等)获取真正的UserPreferences对象。容器将这个代理对象注入到userManager bean中,它不知道这个UserPreferences引用是一个代理。在这个例子中,当UserManager实例调用依赖注入UserPreferences对象上的一个方法时,它实际上是在调用代理上的一个方法。然后代理从HTTP会话(在本例中)获取真实的UserPreferences对象,并将方法调用委托给检索到的真实UserPreferences对象。

Thus, you need the following (correct and complete) configuration when injecting request- and session-scoped beans into collaborating objects, as the following example shows:

因此,当将请求和会话作用域的bean注入到协作对象中时,您需要以下(正确且完整的)配置,如下面的示例所示:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
Choosing the Type of Proxy to Create

By default, when the Spring container creates a proxy for a bean that is marked up with the <aop:scoped-proxy/> element, a CGLIB-based class proxy is created.

默认情况下,当Spring容器为使用<aop:scoped-proxy/>元素标记的bean创建代理时,会创建一个基于cglib的类代理。

CGLIB proxies intercept only public method calls! Do not call non-public methods on such a proxy. They are not delegated to the actual scoped target object.

CGLIB代理只拦截公共方法调用!不要在这样的代理上调用非公共方法。它们没有被委托给实际的作用域目标对象。

Alternatively, you can configure the Spring container to create standard JDK interface-based proxies for such scoped beans, by specifying false for the value of the proxy-target-class attribute of the <aop:scoped-proxy/> element. Using JDK interface-based proxies means that you do not need additional libraries in your application classpath to affect such proxying. However, it also means that the class of the scoped bean must implement at least one interface and that all collaborators into which the scoped bean is injected must reference the bean through one of its interfaces. The following example shows a proxy based on an interface:

方法的proxy-target-class属性的值指定false,也可以配置Spring容器为这种作用域bean创建基于JDK接口的标准代理<aop:scoped-proxy/>元素。使用基于JDK接口的代理意味着您不需要在应用程序类路径中添加其他库来影响这种代理。然而,这也意味着限定作用域的bean必须实现至少一个接口,并且所有被注入限定作用域bean的协作者必须通过它的一个接口引用该bean。下面的例子展示了一个基于接口的代理:
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

For more detailed information about choosing class-based or interface-based proxying, see Proxying Mechanisms.

有关选择基于类或基于接口的代理的详细信息,请参见代理机制。

1.5.5. Custom Scopes

The bean scoping mechanism is extensible. You can define your own scopes or even redefine existing scopes, although the latter is considered bad practice and you cannot override the built-in singleton and prototype scopes.

bean的作用域机制是可扩展的。您可以定义自己的作用域,甚至可以重新定义现有的作用域,尽管后者被认为是不好的实践,并且您不能覆盖内置的singleton和prototype作用域。
Creating a Custom Scope

To integrate your custom scopes into the Spring container, you need to implement the org.springframework.beans.factory.config.Scope interface, which is described in this section. For an idea of how to implement your own scopes, see the Scope implementations that are supplied with the Spring Framework itself and the Scope javadoc, which explains the methods you need to implement in more detail.

为了将您的自定义作用域集成到Spring容器中,您需要实现org.springframework.beans.factory.config.Scope接口,该接口将在本节中描述。要了解如何实现自己的作用域,请参阅Spring框架本身提供的作用域实现和Scope javadoc,后者更详细地解释了需要实现的方法。

The Scope interface has four methods to get objects from the scope, remove them from the scope, and let them be destroyed.

Scope接口有四个方法用于从范围中获取对象、从范围中删除对象和销毁对象。

The session scope implementation, for example, returns the session-scoped bean (if it does not exist, the method returns a new instance of the bean, after having bound it to the session for future reference). The following method returns the object from the underlying scope:

例如,会话范围实现返回会话范围的bean(如果它不存在,该方法将其绑定到会话以便将来引用后返回该bean的新实例)。下面的方法从底层作用域返回对象:
Object get(String name, ObjectFactory<?> objectFactory)

The session scope implementation, for example, removes the session-scoped bean from the underlying session. The object should be returned, but you can return null if the object with the specified name is not found. The following method removes the object from the underlying scope:

例如,会话范围实现从底层会话中删除了会话范围的bean。应该返回该对象,但如果没有找到具有指定名称的对象,则可以返回null。下面的方法将对象从底层作用域中移除:
Object remove(String name)

The following method registers a callback that the scope should invoke when it is destroyed or when the specified object in the scope is destroyed:

下面的方法注册了一个回调函数,当它被销毁或者作用域中的指定对象被销毁时,作用域应该调用它:
void registerDestructionCallback(String name, Runnable destructionCallback)

See the javadoc or a Spring scope implementation for more information on destruction callbacks.

The following method obtains the conversation identifier for the underlying scope:

下面的方法获取底层作用域的对话标识符:
String getConversationId()

This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.

这个标识符对于每个作用域都是不同的。对于会话作用域的实现,该标识符可以是会话标识符。
Using a Custom Scope

After you write and test one or more custom Scope implementations, you need to make the Spring container aware of your new scopes. The following method is the central method to register a new Scope with the Spring container:

在编写和测试一个或多个自定义作用域实现之后,需要让Spring容器知道您的新作用域。下面的方法是向Spring容器注册一个新的Scope的中心方法:
void registerScope(String scopeName, Scope scope);

This method is declared on the ConfigurableBeanFactory interface, which is available through the BeanFactory property on most of the concrete ApplicationContext implementations that ship with Spring.

该方法是在ConfigurableBeanFactory接口上声明的,该接口可以通过Spring附带的大多数具体ApplicationContext实现上的BeanFactory属性使用。

The first argument to the registerScope(..) method is the unique name associated with a scope. Examples of such names in the Spring container itself are singleton and prototype. The second argument to the registerScope(..) method is an actual instance of the custom Scope implementation that you wish to register and use.

registerScope(..)方法的第一个参数是与作用域关联的惟一名称。Spring容器本身中的这类名称的例子有singleton和prototype。registerScope(..)方法的第二个参数是您希望注册和使用的自定义Scope实现的实际实例。

Suppose that you write your custom Scope implementation, and then register it as shown in the next example.

假设您编写了自定义Scope实现,然后按照下一个示例所示注册它。

The next example uses SimpleThreadScope, which is included with Spring but is not registered by default. The instructions would be the same for your own custom Scope implementations.

下一个示例使用SimpleThreadScope,它包含在Spring中,但默认情况下没有注册。对于您自己的自定义Scope实现,指令是相同的。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

You can then create bean definitions that adhere to the scoping rules of your custom Scope, as follows:

然后,您可以创建符合自定义范围的作用域规则的bean定义,如下所示:
<bean id="..." class="..." scope="thread">

With a custom Scope implementation, you are not limited to programmatic registration of the scope. You can also do the Scope registration declaratively, by using the CustomScopeConfigurer class, as the following example shows:

使用自定义Scope实现,您就不局限于以编程方式注册范围。您还可以通过使用CustomScopeConfigurer类来声明地注册Scope,如下面的例子所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>

When you place <aop:scoped-proxy/> within a <bean> declaration for a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().

当您在FactoryBean实现的声明中放置<aop:scoped-proxy/>时,限定范围的是工厂bean本身,而不是从getObject()返回的对象。
posted @ 2022-09-09 09:40  丶Jan  阅读(45)  评论(0)    收藏  举报