1.5. Bean Scopes

上一节 1.4. Dependencies

目录

下一节 1.6. Customizing the Nature of a Bean

1.5. Bean Scopes

1.5. Bean有效范围

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 definition时,您创建了一个实现方法,用于创建由该bean definition定义
的类的实际实例。
bean definition是实现方法的思想很重要,因为这意味着,与类一样,您可以从单个实现
方法创建许多对象实例。

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:

下表描述了支持的范围:

Table 3. Bean scopes
ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototypeScopes a single bean definition to any number of object instances.
requestScopes 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.
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
范围描述
singleton(默认)将每个Spring IoC容器的单个bean definition 定位到单个对象实例.
prototype将单个bean定义作用于任意数量的对象实例。
request将单个bean definition 定位到单个HTTP请求的生命周期。也就是说,每个HTTP请求都有它自己的bean实例,该实例是在单个bean definition 创建的。仅在支持web的Spring ApplicationContext上下文中有效.
session将单个bean definition作用于HTTP会话的生命周期。仅在支持web的Spring ApplicationContext上下文中有效.
application将单个bean定义作用于ServletContext的生命周期。仅在支持web的Spring ApplicationContext上下文中有效.
websocket将单个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
1.5.1. Singleton 类型的作用域

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.

只管理一个singleton bean的共享实例,并且所有对具有一个或多个ID的bean的请求
都与该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 definition 并且它的作用域是 singleton 时,
Spring IoC容器会创建该bean definition 定义的对象的一个实例。
这个单一实例存储在这样的 singleton 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的 singleton bean概念不同于“四人帮模式”一书中定义的单例模式。
GoF单例对对象的作用域进行硬编码,这样每个类加载器都会创建一个且只有一个
特定类的实例。
Spring singleton 的作用域最好描述为每个容器和每个bean。
这意味着,如果您在单个Spring容器中为特定类定义一个bean,则Spring容器将
创建该bean definition定义的类的一个且仅一个实例。
singleton 作用域是Spring中的默认作用域。要在XML中定义一个singleton 的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
1.5.2. Prototype 类型的作用域

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.
The following diagram illustrates the Spring prototype scope:

部署非 singleton 类型的 Prototype类型bean 导致每次对特定bean发出请求时都
创建一个新的bean实例。
也就是说,在将bean注入到另一个bean中,或者通过容器上的getBean()方法调用
请求它的时候。
作为一个规则,您应该对所有有状态的bean使用 Prototype 作用域,对无状态
bean使用 singleton 作用域。
下面的图表说明了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.)
The following example defines a bean as a prototype in XML:


(数据访问对象(DAO)通常不配置为 prototype,因为典型的DAO不持有任何会话状态。
重用 singleton 图的核心对我们来说更容易。)
下面的例子,在XML文件中将bean定义为prototype 类型:

<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不管理 prototype 类型bean的完整生命周期。
容器实例化、配置和组装原型对象并将其交给客户端,而不进一步记录该原型实例。
因此,尽管所有对象的初始化生命周期回调方法都被调用,但prototype bean,
配置的销毁生命周期回调不会被调用。
客户端代码必须清理 prototype 的对象并释放 prototype bean所持有的珍贵资源。
要让Spring容器释放 prototype 作用域bean所持有的资源,请尝试使用
自定义bean 的post-processor方法,它持有对需要清理的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.)

某种意义上来说,对于 prototype 作用域bean的Spring容器角色是Java new
操作符的替代品。
超过这个时间点的所有生命周期管理都必须由客户处理。
(有关Spring容器中bean生命周期的详细信息,请参见生命周期回调。)

1.5.3. Singleton Beans with Prototype-bean Dependencies
1.5.3. Singleton作用域的bean引用 Prototype作用域的bean

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.

当您使用带有 prototype bean依赖项的 Singleton 作用域的bean时,请注意依赖项
是在实例化时解析的。
因此,如果您将 prototype 作用域的bean依赖注入到 Singleton 作用域的bean中,
那么将实例化一个新的 prototype bean,然后依赖地注入到 Singleton bean中。
prototype 实例是提供给 Singleton 作用域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(https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#beans-factory-method-injection)

但是,假设您希望 Singleton 作用域bean在运行时重复获得 prototype 作用域bean
的新实例。
您不能将 prototype 作用域的bean依赖注入到 Singleton bean中,因为该注入
只在Spring容器实例化 Singleton bean并解析和注入其依赖项时发生一次。
如果您不止一次地需要 prototype bean在运行时的新实例,请参阅方法注入。

1.5.4. Request, Session, Application, and WebSocket Scopes
1.5.4. Request, Session, Application, and WebSocket 作用域

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.

请求、会话、应用程序和websocket作用域只有在使用web 的
Spring ApplicationContext实现(如XmlWebApplicationContext)时才可用。
如果您将这些作用域与常规的Spring IoC容器(如ClassPathXmlApplicationContext)
一起使用,则会抛出一个IllegalStateException,该异常报告一个未知的bean作用域。

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.)

为了在request, session, application, websocket级别(web作用域bean)上
支持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.
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

如果您在Spring Web MVC中访问范围限定的bean
(实际上是在由Spring DispatcherServlet处理的请求中访问),
则不需要进行特殊设置。
DispatcherServlet已经公开了所有相关状态。
如果您使用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。
filter mapping取决于web应用程序配置环境,因此您必须适当地更改它。
下面的清单显示了web应用程序的filter部分:

<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请求对象绑定到为该请求提供服务的线程。
这使得作用域为 request 和 session 的bean在调用链的更深处可用。

Request scope
Request作用域

Consider the following XML configuration for a bean definition:

考虑以下针对bean定义的XML配置:

<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 definition 
创建的其他实例不会看到这些状态更改。
它们是针对个别请求的。当请求完成处理时,作用于该请求的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
Session 作用域

Consider the following XML configuration for a bean definition:

考虑以下针对bean definition 的XML配置:

<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 session生存期的UserPreferences bean
定义来创建一个UserPreferences bean的新实例。
换句话说,userPreferences bean有效地限定了HTTP session 级别的范围。
与request 作用域bean一样,你可以尽可能多地按照你想要的方式改变创建
实例内部状态,因为我们知道其他HTTP session 实例也使用相同的 
userPreferences bean definition来创建实例,这些实例看不到这些变化状态,
因为他们是特定于独立的HTTP session。
当最终丢弃HTTP session 时,作用域为该特定HTTP session的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
Application 作用域

Consider the following XML configuration for a bean definition:

考虑以下针对bean定义的XML配置:

<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.

Spring容器为整个web应用程序使用一次AppPreferences bean定义,
从而创建AppPreferences bean的新实例。
也就是说,appPreferences bean的作用域在ServletContext级别,
并存储为一个常规的ServletContext属性。
这有点类似于 Spring singleton bean,但在两个重要方面不同:它是
每一个ServletContext有一个singleton,
不是每一个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
注释来为 application 作用域分配一个组件。
下面的例子展示了如何做到这一点:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
Scoped Beans as Dependencies
将限定范围的bean作为依赖项

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 request 作用域的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.

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


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.

当对作用域 prototype bean 声明  <aop:scoped-proxy/>标签 ,
对共享代理的每个方法调用都会创建一个新的目标实例,
然后将调用转发到该目标实例。

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, 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.

此外,作用域代理并不是以生命周期安全的方式从更短的作用域访问bean的唯一方法。
你也可以声明你的注入点(也就是构造函数或setter参数或autowired的字段)
ObjectFactory <MyTargetBean> 允许getObject()调用来检索当前实例对
需求每次需要——没有分别持有实例或存储它。


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

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

The JSR-330 variant of this is called Provider and is used with a Provider declaration and a corresponding get() call for every retrieval attempt. See here for more details on JSR-330 overall.(https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#beans-standard-annotations)

它的JSR-330变种称为提供者,并与提供者一起使用。声明,
并对每次检索尝试进行相应的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/>  1 
    </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):

要创建这样的代理,需要向bean definition插入一个子元素<aop:scoped-proxy/> 
(请参阅章节 - 选择要创建的代理类型和基于XML模式的配置)。
为什么在request, session 和 custom-scope作用域级别定义bean需要
<aop:scoped-proxy/>元素?
考虑下面的 singleton bean定义,并将其与您需要为上述范围定义的对象进行对比
(注意,下面的userPreferences bean definition 是不完整的):

<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.

在前面的示例中,singleton bean (userManager)被注入到了HTTP session 
作用域bean (userPreferences)的引用。
这里要关注的要点是userManager bean是singleton 类型:每个容器只实例化一次,
它的依赖项(在本例中只有一个,即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作为依赖项注入到 singleton bean中),
这不是您想要的行为。
相反,您需要一个userManager对象,并且在HTTP session 的生命周期中,
您需要一个特定于HTTP session 的userPreferences对象。
因此,容器创建一个对象,该对象公开与UserPreferences类完全相同的公共接口
(理想情况下,该对象是一个UserPreferences实例),
它可以从作用域机制(HTTP request、session 等)获取真正的UserPreferences对象。
容器将这个代理对象注入到userManager bean中,
该bean不知道这个UserPreferences引用是一个代理。
在这个例子中,当UserManager实例调用依赖注入的UserPreferences对象上的方法时,
它实际上是调用代理上的方法。
代理然后从(在本例中)HTTP session中获取真实的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:

因此,当将 request 和 session 作用域的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:

或者,您可以配置Spring容器为这种作用域bean创建标准的基于JDK接口的代理,
方法是将 <aop:scoped-proxy/> 属性的值指定为false。
使用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.(https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#aop-proxying)

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

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.bean.factory.config.Scope接口,这将在本节中描述。
要了解如何实现自己的作用域,请参阅随Spring框架本身提供的作用域实现和作用域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:

例如,session 作用域的实现返回 session 作用域bean(如果它不存在,
该方法将bean的一个新实例绑定到session 以供将来引用)。
下面的方法从底层范围返回对象:

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:

例如,session 作用域实现从基础 session 中删除session 作用域bean。
应该返回该对象,但如果没有找到具有指定名称的对象,则可以返回null。
下面的方法将对象从底层范围中移除:

Object remove(String name)

The following method registers the callbacks the scope should execute 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:

有关销毁回调的更多信息,请参阅javadoc或Spring作用域实现。
下面的方法获取底层范围的对话标识符:

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容器注册新范围的中心方法:

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(..)方法的第二个参数是您希望注册和使用的自定义范围实现的一个实际实例。

Suppose that you write your custom Scope implementation, and then register it as shown in the next example.
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 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 definitions ,如下所示:

<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:

使用自定义范围实现,您不必局限于对范围的编程式注册。
您还可以通过使用CustomScopeConfigurer类声明性地进行范围注册,
如下面的示例所示:

<?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/ in a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().

当你在FactoryBean实现中设置<aop:scoped-proxy/>,作用域是工厂bean本身,
而不是getObject()返回的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值