Spring 5.0.0框架介绍_中英文对照_3.5

文章作者:Tyan
博客:noahsnail.com

3.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 the scope of the objects created from a particular bean definition. This approach is powerful and flexible in that 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: out of the box, the Spring Framework supports six scopes, five of which are available only if you use a web-aware ApplicationContext.

你不仅能管理要插入对象中的的各种依赖和配置值,而且能管理对象的作用域,对象是从特定的bean定义中创建的。这种方法是强大且灵活的,你可以通过配置文件选择你创建的对象的作用域,从而代替Java类级别对象的内置作用域。定义的beans将部署成多种作用域中的一种:开箱即用,Spring框架支持六种作用域,如果你使用感知web的ApplicationContext,你只可以使用其中的五种作用域。

The following scopes are supported out of the box. You can also create a custom scope.

下面的作用域支持开箱即用。你也可以创建一个定制的作用域。

Table 3.3. Bean scopes

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance per Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
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.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware 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.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

表 3.3 bean作用域

作用域 描述
singleton (默认) 每个Spring IoC容器使单个bean定义只能创建一个对象实例。
prototype 单个bean定义可以创建任何数量的对象实例。
request 单个bean定义的创建实例的作用域为单个HTTP request的声明周期;也就是说,每个HTTP request有它自己的根据bean定义创建的实例。只在感知Spring ApplicationContext的上下文中有效。
session 单个bean定义的创建实例的作用域为HTTP Session的生命周期. 只在感知Spring ApplicationContext的上下文中有效。
application 单个bean定义的创建实例的作用域为ServletContext的生命周期。 只在感知Spring ApplicationContext的上下文中有效。
websocket 单个bean定义的创建实例的作用域为WebSocket的生命周期。 只在感知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 the section called “Using a custom scope”.

 

从Spring 3.0,引入了thread scope作用域,但默认情况下是不注册的。更多的信息请看SimpleThreadScope文档。关于怎么注册thread scope作用域或任何其它的定制作用域的介绍,请看『Using a custom scope』小节。

3.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 matching that bean definition result in that one specific bean instance being returned by the Spring container.

单例bean只管理一个共享实例,id匹配bean定义的所有对beans的请求,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.

换言之,当你定义一个bean定义时,它的作用域为单例,Spring IoC容器会根据bean定义创建一个确定的对象实例。这个单独的实例存储在单例beans的缓存中,接下来的对这个命名bean的所有请求和引用都会返回那个缓存的对象。

image

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 per container and per bean. This means that if you define one bean for a particular class in a single Spring container, then 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 would write, for example:

Spring中的单例bean概念不同于《设计模式》书中定义的单例模式。设计模式中的单例是对对象的作用域进行硬编码,为的是每个类加载器只能创建一个特定类的实例。Spring单例作用域最好的描述是每个容器每个类。这意味着如果你在单个的Spring容器中为一个特定的类定义了一个bean,Spring只会根据bean定义创建一个类的实例。在Spring中单例作用域是默认的作用域。为了在XML定义一个单例bean,你可以像下面一样写,例如:

1
2
3
4
<bean id="accountService" class="com.foo.DefaultAccountService"/>

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

3.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, use the prototype scope for all stateful beans and the singleton scope for stateless beans.

非单例模式,bean部署采用原型作用域时,每次产生一个特定bean的请求时都会创建一个新的bean实例。也就是说,这个bean会注入到另一个bean中或你可以在容器中通过调用getBean()方法来请求它。通常,对于所有有状态的beans使用原型作用域,对于无状态的beans使用单例作用域。

The following diagram illustrates the Spring prototype scope. A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state; it was just easier for this author to reuse the core of the singleton diagram.

下面的图阐述了Spring原型作用域。数据访问对象(DAO)通常是不会配置为原型的,因为一个典型的DAO不会有任何会话状态;对于作者来说很容易重用单例图的核心。

image

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

下面的例子在XML中定义一个原型bean:

1
<bean id="accountService" class="com.foo.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 bean(s) are holding. 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 Section 3.6.1, “Lifecycle callbacks”.)

在有些方面,关于原型作用域,Spring容器的角色像是Java中new操作符的替代品。所有生命周期的管理必须由客户端处理。(Spring容器中更多关于bean生命周期的细节,请看3.6.1小节,”生命周期回调”)。

3.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实例曾经是唯一提供给单例作用域的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 is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Section 3.4.6, “Method injection”.

假设你想在运行时让单例作用域的bean重复的获得原型作用域bean的新实例。你不能依赖注入原型作用域的bean到你的单例bean中,因为当Spring容器实例化单例bean,解析并注入它的依赖时,注入只发生一次。如果你在运行时不止一次需要原型bean的实例,请看3.4.6小节,”方法注入”。

3.5.4 Request, session, application, and WebSocket scopes

The request, session, application, and websocket scopes are only available 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 will be thrown complaining about an unknown bean scope.

如果你使用感知web的Spring ApplicationContext实现(例如XmlWebApplicationContext),requestsessionapplicationwebsocket作用域是唯一可用的作用域。如果你通过正规的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.)

为了支持requestsessionapplicationwebsocket标准的bean作用域,在你定义你的bean之前需要进行一些较小的初始化配置。(对于标准作用域singletonprototype,初始化设置不需要的。)

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, then no special setup is necessary: DispatcherServlet already exposes all relevant state.

如果你在Spring Web MVC中访问具有作用域的beans,请求内部是通过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 via 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.xml文件中添加下面的声明来代替:

1
2
3
4
5
6
7
8
9
<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.

如果你的监听器设置有问题,作为一种选择,你可以考虑Spring的RequestContextFilter。过滤器映射依赖于web应用程序的相关配置,因此你必须适当的更改它。

1
2
3
4
5
6
7
8
9
10
11
12
<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.

DispatcherServletRequestContextListenerRequestContextFilter都是在做同样的事,也就是说将HTTP请求对象绑定到服务请求的Thread上。这使得request作用域和session作用域的beans在更深一层的调用链中是可用的。

Request scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="loginAction" class="com.foo.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 will 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.

对于每一个HTTP请求,Spring容器通过使用loginAction定义创建一个新的LoginAction bean实例。也就是说,loginAction bean的作用域是在HTTP request级别的。你可以任意改变创建的实例的内部状态,因为其它的根据loginAction bean定义创建的实例不会看到这些状态的改变;它们对于每个单独的请求都是独有的。当请求处理完成时,请求作用域的bean被销毁。

When using annotation-driven components or Java Config, the @RequestScope annotation can be used to assign a component to the request scope.

当使用注解驱动的组件或Java配置时,@RequestScope注解能用来指定一个组件的作用域为request。

1
2
3
4
5
@RequestScope
@Component
public class LoginAction {
// ...
}

Session scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="userPreferences" class="com.foo.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.

对于单个HTTP Session的生命周期,Spring容器通过userPreferences bean定义创建一个UserPreferences bean实例。换句话说,userPreferences bean的有效作用域是HTTP Session级别的。正如request作用域的beans一样,你可以任意改变你想改变的创建的bean实例的内部状态,知道其它的使用根据userPreferences bean定义创建的HTTP Session实例也不会看到这些内部状态的改变,因为它们对于每个单独的HTTP Session都是独有的。当HTTP Session被最终销毁时,Session作用域的bean也被销毁。

When using annotation-driven components or Java Config, the @SessionScope annotation can be used to assign a component to the session scope.

当使用注解驱动的组件或Java配置时,@SessionScope注解能用来指定一个组件的作用域为session。

1
2
3
4
5
@SessionScope
@Component
public class UserPreferences {
// ...
}

Application scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="appPreferences" class="com.foo.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, 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应用而言,Spring容器根据appPreferences bean定义只创建一次AppPreferences bean的新实例。也就是说,appPreferences bean的作用域是ServletContext级别的,作为一个正规的ServletContext特性来存储。这有点类似于Spring的单例bean,但在两个方面是不同的:它对于每个ServletContext是单例的,而不是每个Spring ApplicationContext(在任何给定的web应用中可能有几个ApplicationContext),它是真正显露的,因此作为一个ServletContext特性是可见的。

When using annotation-driven components or Java Config, the @ApplicationScope annotation can be used to assign a component to the application scope.

当使用注解驱动的组件或Java配置时,@ApplicationScope注解能用来指定一个组件的作用域为Application。

1
2
3
4
5
@ApplicationScope
@Component
public class AppPreferences {
// ...
}

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容器不仅管理对象的实例化,而且管理协作者(或依赖)的绑定。例如,如果你想将一个具有HTTP request作用域的bean注入到另一个具有更长生命周期作用域的bean中,你可以选择注入一个AOP代理来代替具有作用域的bean。也就是说,你需要注入一个代理对象,这个对象能显露与具有作用域的对象相同的接口,但也能从相关的作用域中(例如HTTP request作用域)得到真正的目标对象,能通过委派方法调用到真正的对象。

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 will lead to the creation of a new target instance which the call is then being forwarded to.

Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also simply declare your injection point (i.e. the constructor/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.

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

 

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

当将作用域为prototype的bean声明为<aop:scoped-proxy/>时,每个在共享代理上的方法调用会引起一个新目标实例(调用朝向的)的创建。

通过生命周期安全的方式访问更短的作用域中beans,作用域代理也不是唯一的方式。你也可以简单的声明你的注入点(例如,构造函数/setter参数或自动装配领域)为ObjectFactory<MyTargetBean>,考虑到每次需要的时候通过getObject()调用来取得索要的当前实例——没有分别控制实例或储存它。

JSR-300变量被称作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.

下面例子中的配置只有一行,但对于理解它背后的”why”和”how”是重要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.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.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>

To create such a proxy, you insert a child <aop:scoped-proxy/> element into a scoped bean definition (see the section called “Choosing the type of proxy to create” and Chapter 38, XML Schema-based configuration). Why do definitions of beans scoped at the request, session and custom-scope levels require the <aop:scoped-proxy/> element? Let’s examine 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定义中(看”选择创建的代理类型”小节和38章,基于XML Schema的配置)。为什么bean定义的作用域为requestsession和定制作用域级别需要<aop:scoped-proxy/>元素?让我们检查下面的单例bean定义,并将它与你需要定义的前面提到的作用域进行比较(注意下面的userPreferences bean定义按目前情况是不完全的)。

1
2
3
4
5
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.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 will be 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 will only operate on the exact same userPreferences object, that is, the one that it was originally injected with.

在上面的例子中,单例bean userManager通过引用被注入到具有HTTP Session作用域的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 said 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, etc.). 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 actually is 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 Session的生命周期,你需要一个特定的被称为HTTP SessionuserPreferences对象。因此容器创建了一个与UserPreferences类暴露相同的公共接口的对象(理想情况下是一个UserPreferences实例),这个对象能从作用域机制中(HTTP request,Session等)取得真正的UserPreferences对象。容器将这个代理对象注入到userManager bean中,userManager 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:

因此当注入具有request或session作用域的bean到协作对象中时,你需要下面的,正确的,完整的配置:

1
2
3
4
5
6
7
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.foo.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 only intercept public method calls! Do not call non-public methods on such a proxy; they will not be 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 effect 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.

作为一种选择,对于这种具有作用域的bean你可以配置Spring容器创建标准JDK基于接口的代理,通过指定<aop:scoped-proxy/>元素的proxy-target-class特定的值为false。使用JDK基于接口的代理意味着在你应用程序类路径中你不需要额外的库来支持这种代理的使用。然而,它也意味着具有作用域的bean的类必须实现至少一个接口,并且注入这个bean的所有协作者必须通过它接口中的一个来引用它。

1
2
3
4
5
6
7
8
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>

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

For more detailed information about choosing class-based or interface-based proxying, see Section 7.6, “Proxying mechanisms”.

关于选择基于类或基于接口代理的更多细节信心,请看7.6小节,”代理机制”。

3.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 scope(s) 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 javadocs, which explains the methods you need to implement in more detail.

为了将你的定制作用域集成到Spring容器中,你需要实现org.springframework.beans.factory.config.Scope接口,这一节将描述这个接口。对于怎样实现你自己作用域的想法,请看Spring框架本身提供的Scope实现和Scope文档,它们解释了你需要实现的方法的更多细节。

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

Scope接口有四个方法,从作用域中取得对象,从作用域中移除对象,并且允许它们被销毁。

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

下面的方法从潜在的作用域返回对象。以session作用域实现为例,返回具有session作用域的bean(如果它不存在,这个方法返回一个bean的新实例,然后绑定到session中准备将来引用)。

1
Object get(String name, ObjectFactory objectFactory)

The following method removes the object from the underlying scope. 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.

下面的方法从潜在作用域中移除对象。以session作用域实现为例,从潜在的session中移除session作用域的bean。对象应该被返回,但如果没有找到指定名字的对象会返回空。

1
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. Refer to the javadocs or a Spring scope implementation for more information on destruction callbacks.

下面的方法是注册当作用域销毁时或当作用域中的指定对象销毁时,作用域应该执行的回调函数。销毁回调函数的更多信息请看文档或Spring作用域实现。

1
void registerDestructionCallback(String name, Runnable destructionCallback)

The following method obtains the conversation identifier for the underlying scope. This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.

下面的方法是获得潜在作用域的会话标识符。每个作用域的标识符都是不同的。对于session作用域实现,标识符是session标识符。

1
String getConversationId()

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 scope(s). The following method is the central method to register a new Scope with the Spring container:

在你编写和测试一个或多个定制Scope实现之后,你需要让Spring容器感知到你的新作用域。下面是在Spring容器中注册一个新Scope的主要方法:

1
void registerScope(String scopeName, Scope scope);

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

这个方法是在ConfigurableBeanFactory接口中声明的,在大多数具体的ApplicationContext实现中都可获得,在Spring中通过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容器本身中的这种名字的例子是singletonprototyperegisterScope(..)方法中的第二个参数是你想注册和使用的定制Scope实现的真正实例。

Suppose that you write your custom Scope implementation, and then register it as below.

假设你编写了你的定制Scope实现并按如下注册。

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

 

下面的例子使用Spring包含的SimpleThreadScope,但默认是不注册的。这个用法说明与你自己的定制Scope是一样的。

1
2
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

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

然后创建具有你自己定制的Scope规则的bean定义:

1
<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, using the CustomScopeConfigurer class:

在定制Scope实现后,你不会受限于作用域的程序注册。你也可以声明式的进行Scope注册,使用CustomScopeConfigurer类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://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="bar" class="x.y.Bar" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>

<bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</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()中返回的对象。

如果有收获,可以请我喝杯咖啡!