Spring 5.0.0框架介紹_中英文對(duì)照_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.

當(dāng)你創(chuàng)建bean定義時(shí),你創(chuàng)建了一個(gè)配方用于創(chuàng)建bean定義中定義的類的實(shí)例。bean定義是配方的想法是很重要的,因?yàn)檫@意味著對(duì)于一個(gè)類,你可以根據(jù)一個(gè)配方創(chuàng)建許多對(duì)象實(shí)例。

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.

你不僅能管理要插入對(duì)象中的的各種依賴和配置值,而且能管理對(duì)象的作用域,對(duì)象是從特定的bean定義中創(chuàng)建的。這種方法是強(qiáng)大且靈活的,你可以通過配置文件選擇你創(chuàng)建的對(duì)象的作用域,從而代替Java類級(jí)別對(duì)象的內(nèi)置作用域。定義的beans將部署成多種作用域中的一種:開箱即用,Spring框架支持六種作用域,如果你使用感知web的ApplicationContext,你只可以使用其中的五種作用域。

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

下面的作用域支持開箱即用。你也可以創(chuàng)建一個(gè)定制的作用域。

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 (默認(rèn)) 每個(gè)Spring IoC容器使單個(gè)bean定義只能創(chuàng)建一個(gè)對(duì)象實(shí)例。
prototype 單個(gè)bean定義可以創(chuàng)建任何數(shù)量的對(duì)象實(shí)例。
request 單個(gè)bean定義的創(chuàng)建實(shí)例的作用域?yàn)閱蝹€(gè)HTTP request的聲明周期;也就是說,每個(gè)HTTP request有它自己的根據(jù)bean定義創(chuàng)建的實(shí)例。只在感知Spring ApplicationContext的上下文中有效。
session 單個(gè)bean定義的創(chuàng)建實(shí)例的作用域?yàn)镠TTP Session的生命周期. 只在感知Spring ApplicationContext的上下文中有效。
application 單個(gè)bean定義的創(chuàng)建實(shí)例的作用域?yàn)?code>ServletContext的生命周期。 只在感知Spring ApplicationContext的上下文中有效。
websocket 單個(gè)bean定義的創(chuàng)建實(shí)例的作用域?yàn)?code>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作用域,但默認(rèn)情況下是不注冊(cè)的。更多的信息請(qǐng)看SimpleThreadScope文檔。關(guān)于怎么注冊(cè)thread scope作用域或任何其它的定制作用域的介紹,請(qǐng)看『Using a custom scope』小節(jié)。

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只管理一個(gè)共享實(shí)例,id匹配bean定義的所有對(duì)beans的請(qǐng)求,Spring容器會(huì)返回一個(gè)特定的bean實(shí)例。

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.

換言之,當(dāng)你定義一個(gè)bean定義時(shí),它的作用域?yàn)閱卫?,Spring IoC容器會(huì)根據(jù)bean定義創(chuàng)建一個(gè)確定的對(duì)象實(shí)例。這個(gè)單獨(dú)的實(shí)例存儲(chǔ)在單例beans的緩存中,接下來的對(duì)這個(gè)命名bean的所有請(qǐng)求和引用都會(huì)返回那個(gè)緩存的對(duì)象。

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概念不同于《設(shè)計(jì)模式》書中定義的單例模式。設(shè)計(jì)模式中的單例是對(duì)對(duì)象的作用域進(jìn)行硬編碼,為的是每個(gè)類加載器只能創(chuàng)建一個(gè)特定類的實(shí)例。Spring單例作用域最好的描述是每個(gè)容器每個(gè)類。這意味著如果你在單個(gè)的Spring容器中為一個(gè)特定的類定義了一個(gè)bean,Spring只會(huì)根據(jù)bean定義創(chuàng)建一個(gè)類的實(shí)例。在Spring中單例作用域是默認(rèn)的作用域。為了在XML定義一個(gè)單例bean,你可以像下面一樣寫,例如:

<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部署采用原型作用域時(shí),每次產(chǎn)生一個(gè)特定bean的請(qǐng)求時(shí)都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。也就是說,這個(gè)bean會(huì)注入到另一個(gè)bean中或你可以在容器中通過調(diào)用getBean()方法來請(qǐng)求它。通常,對(duì)于所有有狀態(tài)的beans使用原型作用域,對(duì)于無狀態(tài)的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原型作用域。數(shù)據(jù)訪問對(duì)象(DAO)通常是不會(huì)配置為原型的,因?yàn)橐粋€(gè)典型的DAO不會(huì)有任何會(huì)話狀態(tài);對(duì)于作者來說很容易重用單例圖的核心。

image

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

下面的例子在XML中定義一個(gè)原型bean:

<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的完整生命周期:容器初始化、配置,另外組裝原型對(duì)象,并把它傳遞給客戶端,之后不再記錄原型實(shí)例。因此,雖然不管什么作用域初始化生命周期回調(diào)函數(shù)都會(huì)在所有對(duì)象上調(diào)用,但是在原型作用域的情況下,不會(huì)調(diào)用配置的銷毀生命周期回調(diào)函數(shù)??蛻舳舜a必須清理原型作用域的對(duì)象并釋放原型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”.)

在有些方面,關(guān)于原型作用域,Spring容器的角色像是Java中new操作符的替代品。所有生命周期的管理必須由客戶端處理。(Spring容器中更多關(guān)于bean生命周期的細(xì)節(jié),請(qǐng)看3.6.1小節(jié),"生命周期回調(diào)")。

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.

當(dāng)你使用含有原型bean依賴的單例作用域bean時(shí),要意識(shí)到依賴解析是在實(shí)例化時(shí)。因此如果你使用依賴注入將原型作用域的bean注入到單例作用域的bean中時(shí),將會(huì)實(shí)例化一個(gè)新的原型bean并依賴注入到單例bean中。原型bean實(shí)例曾經(jīng)是唯一提供給單例作用域的bean的實(shí)例。

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

假設(shè)你想在運(yùn)行時(shí)讓單例作用域的bean重復(fù)的獲得原型作用域bean的新實(shí)例。你不能依賴注入原型作用域的bean到你的單例bean中,因?yàn)楫?dāng)Spring容器實(shí)例化單例bean,解析并注入它的依賴時(shí),注入只發(fā)生一次。如果你在運(yùn)行時(shí)不止一次需要原型bean的實(shí)例,請(qǐng)看3.4.6小節(jié),"方法注入"。

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實(shí)現(xiàn)(例如XmlWebApplicationContext),request,session,applicationwebsocket作用域是唯一可用的作用域。如果你通過正規(guī)的Spring IoC容器例如ClassPathXmlApplicationContext來使用這些作用域,會(huì)拋出IllegalStateException異常,投訴使用了一個(gè)未知的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,applicationwebsocket標(biāo)準(zhǔn)的bean作用域,在你定義你的bean之前需要進(jìn)行一些較小的初始化配置。(對(duì)于標(biāo)準(zhǔn)作用域singletonprototype,初始化設(shè)置不需要的。)

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

怎樣完成這個(gè)初始化設(shè)置依賴于你特定的Servlet環(huán)境。

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,請(qǐng)求內(nèi)部是通過Spring的DispatcherServlet來處理的,不需要特定設(shè)置:DispatcherServlet已經(jīng)顯示了所有相關(guān)的狀態(tài)。

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之外處理請(qǐng)求(例如使用JSF或Struts時(shí)),你需要注冊(cè)org.springframework.web.context.request.RequestContextListener ServletRequestListener。對(duì)于Servlet 3.0+,能通過WebApplicationInitializer接口以編程方式處理。對(duì)于更早的容器,可以在應(yīng)用程序的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.

如果你的監(jiān)聽器設(shè)置有問題,作為一種選擇,你可以考慮Spring的RequestContextFilter。過濾器映射依賴于web應(yīng)用程序的相關(guān)配置,因此你必須適當(dāng)?shù)母乃?/p>

<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,RequestContextListenerRequestContextFilter都是在做同樣的事,也就是說將HTTP請(qǐng)求對(duì)象綁定到服務(wù)請(qǐng)求的Thread上。這使得request作用域和session作用域的beans在更深一層的調(diào)用鏈中是可用的。

Request scope

Consider the following XML configuration for a bean definition:

考慮下面的bean定義的XML配置:

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

對(duì)于每一個(gè)HTTP請(qǐng)求,Spring容器通過使用loginAction定義創(chuàng)建一個(gè)新的LoginAction bean實(shí)例。也就是說,loginAction bean的作用域是在HTTP request級(jí)別的。你可以任意改變創(chuàng)建的實(shí)例的內(nèi)部狀態(tài),因?yàn)槠渌母鶕?jù)loginAction bean定義創(chuàng)建的實(shí)例不會(huì)看到這些狀態(tài)的改變;它們對(duì)于每個(gè)單獨(dú)的請(qǐng)求都是獨(dú)有的。當(dāng)請(qǐng)求處理完成時(shí),請(qǐng)求作用域的bean被銷毀。

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

當(dāng)使用注解驅(qū)動(dòng)的組件或Java配置時(shí),@RequestScope注解能用來指定一個(gè)組件的作用域?yàn)閞equest。

@RequestScope
@Component
public class LoginAction {
    // ...
}

Session scope

Consider the following XML configuration for a bean definition:

考慮下面的bean定義的XML配置:

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

對(duì)于單個(gè)HTTP Session的生命周期,Spring容器通過userPreferences bean定義創(chuàng)建一個(gè)UserPreferences bean實(shí)例。換句話說,userPreferences bean的有效作用域是HTTP Session級(jí)別的。正如request作用域的beans一樣,你可以任意改變你想改變的創(chuàng)建的bean實(shí)例的內(nèi)部狀態(tài),知道其它的使用根據(jù)userPreferences bean定義創(chuàng)建的HTTP Session實(shí)例也不會(huì)看到這些內(nèi)部狀態(tài)的改變,因?yàn)樗鼈儗?duì)于每個(gè)單獨(dú)的HTTP Session都是獨(dú)有的。當(dāng)HTTP Session被最終銷毀時(shí),Session作用域的bean也被銷毀。

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

當(dāng)使用注解驅(qū)動(dòng)的組件或Java配置時(shí),@SessionScope注解能用來指定一個(gè)組件的作用域?yàn)閟ession。

@SessionScope
@Component
public class UserPreferences {
    // ...
}

Application scope

Consider the following XML configuration for a bean definition:

考慮下面的bean定義的XML配置:

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

對(duì)于整個(gè)web應(yīng)用而言,Spring容器根據(jù)appPreferences bean定義只創(chuàng)建一次AppPreferences bean的新實(shí)例。也就是說,appPreferences bean的作用域是ServletContext級(jí)別的,作為一個(gè)正規(guī)的ServletContext特性來存儲(chǔ)。這有點(diǎn)類似于Spring的單例bean,但在兩個(gè)方面是不同的:它對(duì)于每個(gè)ServletContext是單例的,而不是每個(gè)Spring ApplicationContext(在任何給定的web應(yīng)用中可能有幾個(gè)ApplicationContext),它是真正顯露的,因此作為一個(gè)ServletContext特性是可見的。

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

當(dāng)使用注解驅(qū)動(dòng)的組件或Java配置時(shí),@ApplicationScope注解能用來指定一個(gè)組件的作用域?yàn)锳pplication。

@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容器不僅管理對(duì)象的實(shí)例化,而且管理協(xié)作者(或依賴)的綁定。例如,如果你想將一個(gè)具有HTTP request作用域的bean注入到另一個(gè)具有更長(zhǎng)生命周期作用域的bean中,你可以選擇注入一個(gè)AOP代理來代替具有作用域的bean。也就是說,你需要注入一個(gè)代理對(duì)象,這個(gè)對(duì)象能顯露與具有作用域的對(duì)象相同的接口,但也能從相關(guān)的作用域中(例如HTTP request作用域)得到真正的目標(biāo)對(duì)象,能通過委派方法調(diào)用到真正的對(duì)象。

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.

?

你也可以在作用域?yàn)?code>singleton的beans之間使用<aop:scoped-proxy/>,將通過中間代理的引用進(jìn)行序列化,因此能通過反序列化重新獲得目標(biāo)的單例bean。

當(dāng)將作用域?yàn)?code>prototype的bean聲明為<aop:scoped-proxy/>時(shí),每個(gè)在共享代理上的方法調(diào)用會(huì)引起一個(gè)新目標(biāo)實(shí)例(調(diào)用朝向的)的創(chuàng)建。

通過生命周期安全的方式訪問更短的作用域中beans,作用域代理也不是唯一的方式。你也可以簡(jiǎn)單的聲明你的注入點(diǎn)(例如,構(gòu)造函數(shù)/setter參數(shù)或自動(dòng)裝配領(lǐng)域)為ObjectFactory<MyTargetBean>,考慮到每次需要的時(shí)候通過getObject()調(diào)用來取得索要的當(dāng)前實(shí)例——沒有分別控制實(shí)例或儲(chǔ)存它。

JSR-300變量被稱作Provider,對(duì)于每一次取回嘗試使用Provider<MyTargetBean>聲明和對(duì)應(yīng)的get()調(diào)用。關(guān)于JSR-330整體的更多細(xì)節(jié)請(qǐng)看這兒。

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.

下面例子中的配置只有一行,但對(duì)于理解它背后的"why"和"how"是重要的。

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

為了創(chuàng)建這樣一個(gè)代理,你插入一個(gè)子元素<aop:scoped-proxy/>到具有作用域的bean定義中(看"選擇創(chuàng)建的代理類型"小節(jié)和38章,基于XML Schema的配置)。為什么bean定義的作用域?yàn)?code>request,session和定制作用域級(jí)別需要<aop:scoped-proxy/>元素?讓我們檢查下面的單例bean定義,并將它與你需要定義的前面提到的作用域進(jìn)行比較(注意下面的userPreferences bean定義按目前情況是不完全的)。

<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中。這的突出點(diǎn)是userManager bean是單例:每個(gè)容器它將確定的被實(shí)例化一次,它的依賴(在這個(gè)例子中只有一個(gè),userPreferences bean)也只注入一次。這意味著userManager bean只能對(duì)確定的同一個(gè)userPreferences對(duì)象進(jìn)行操作,也就是最初注入的那個(gè)對(duì)象。

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.

當(dāng)將一個(gè)短期作用域的bean注入到一個(gè)長(zhǎng)期作用域的bean中時(shí),這不是你想要的行為,例如將一個(gè)具有HTTP Session作用域的協(xié)作bean作為一個(gè)依賴注入到一個(gè)單例bean中。當(dāng)然,你需要一個(gè)單一的userManager對(duì)象,對(duì)于HTTP Session的生命周期,你需要一個(gè)特定的被稱為HTTP SessionuserPreferences對(duì)象。因此容器創(chuàng)建了一個(gè)與UserPreferences類暴露相同的公共接口的對(duì)象(理想情況下是一個(gè)UserPreferences實(shí)例),這個(gè)對(duì)象能從作用域機(jī)制中(HTTP request,Session等)取得真正的UserPreferences對(duì)象。容器將這個(gè)代理對(duì)象注入到userManager bean中,userManager bean不會(huì)意識(shí)到UserPreferences引用是一個(gè)代理。在這個(gè)例子中,當(dāng)UserManager實(shí)例調(diào)用依賴注入的UserPreferences對(duì)象的方法時(shí),它實(shí)際上調(diào)用的是代理中的一個(gè)方法。代理能從HTTP Session中(在這個(gè)例子)取得真正的UserPreferences對(duì)象,將方法調(diào)用委托到取得的真正的UserPreferences對(duì)象上。

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

因此當(dāng)注入具有request或session作用域的bean到協(xié)作對(duì)象中時(shí),你需要下面的,正確的,完整的配置:

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

當(dāng)Spring容器為具有<aop:scoped-proxy/>標(biāo)記的bean創(chuàng)建代理時(shí),默認(rèn)情況下,創(chuàng)建一個(gè)基于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代理只攔截公有方法調(diào)用。在這個(gè)代理上不調(diào)用非公有方法;它們不能委托給實(shí)際作用域目標(biāo)對(duì)象。

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.

作為一種選擇,對(duì)于這種具有作用域的bean你可以配置Spring容器創(chuàng)建標(biāo)準(zhǔn)JDK基于接口的代理,通過指定<aop:scoped-proxy/>元素的proxy-target-class特定的值為false。使用JDK基于接口的代理意味著在你應(yīng)用程序類路徑中你不需要額外的庫來支持這種代理的使用。然而,它也意味著具有作用域的bean的類必須實(shí)現(xiàn)至少一個(gè)接口,并且注入這個(gè)bean的所有協(xié)作者必須通過它接口中的一個(gè)來引用它。

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

關(guān)于選擇基于類或基于接口代理的更多細(xì)節(jié)信心,請(qǐng)看7.6小節(jié),"代理機(jī)制"。

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作用域機(jī)制是可擴(kuò)展的;你可以定義你自己的作用域,甚至重新定義現(xiàn)有的作用域,雖然后者被認(rèn)為是一種不好的實(shí)踐,你不能覆蓋內(nèi)置的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容器中,你需要實(shí)現(xiàn)org.springframework.beans.factory.config.Scope接口,這一節(jié)將描述這個(gè)接口。對(duì)于怎樣實(shí)現(xiàn)你自己作用域的想法,請(qǐng)看Spring框架本身提供的Scope實(shí)現(xiàn)和Scope文檔,它們解釋了你需要實(shí)現(xiàn)的方法的更多細(xì)節(jié)。

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

Scope接口有四個(gè)方法,從作用域中取得對(duì)象,從作用域中移除對(duì)象,并且允許它們被銷毀。

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

下面的方法從潛在的作用域返回對(duì)象。以session作用域?qū)崿F(xiàn)為例,返回具有session作用域的bean(如果它不存在,這個(gè)方法返回一個(gè)bean的新實(shí)例,然后綁定到session中準(zhǔn)備將來引用)。

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.

下面的方法從潛在作用域中移除對(duì)象。以session作用域?qū)崿F(xiàn)為例,從潛在的session中移除session作用域的bean。對(duì)象應(yīng)該被返回,但如果沒有找到指定名字的對(duì)象會(huì)返回空。

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.

下面的方法是注冊(cè)當(dāng)作用域銷毀時(shí)或當(dāng)作用域中的指定對(duì)象銷毀時(shí),作用域應(yīng)該執(zhí)行的回調(diào)函數(shù)。銷毀回調(diào)函數(shù)的更多信息請(qǐng)看文檔或Spring作用域?qū)崿F(xiàn)。

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.

下面的方法是獲得潛在作用域的會(huì)話標(biāo)識(shí)符。每個(gè)作用域的標(biāo)識(shí)符都是不同的。對(duì)于session作用域?qū)崿F(xiàn),標(biāo)識(shí)符是session標(biāo)識(shí)符。

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:

在你編寫和測(cè)試一個(gè)或多個(gè)定制Scope實(shí)現(xiàn)之后,你需要讓Spring容器感知到你的新作用域。下面是在Spring容器中注冊(cè)一個(gè)新Scope的主要方法:

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.

這個(gè)方法是在ConfigurableBeanFactory接口中聲明的,在大多數(shù)具體的ApplicationContext實(shí)現(xiàn)中都可獲得,在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(..)方法中的第一個(gè)參數(shù)是關(guān)于作用域的唯一名字;Spring容器本身中的這種名字的例子是singletonprototyperegisterScope(..)方法中的第二個(gè)參數(shù)是你想注冊(cè)和使用的定制Scope實(shí)現(xiàn)的真正實(shí)例。

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

假設(shè)你編寫了你的定制Scope實(shí)現(xiàn)并按如下注冊(cè)。

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,但默認(rèn)是不注冊(cè)的。這個(gè)用法說明與你自己的定制Scope是一樣的。

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

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

然后創(chuàng)建具有你自己定制的Scope規(guī)則的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, using the CustomScopeConfigurer class:

在定制Scope實(shí)現(xiàn)后,你不會(huì)受限于作用域的程序注冊(cè)。你也可以聲明式的進(jìn)行Scope注冊(cè),使用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
        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().

?

當(dāng)你在FactoryBean實(shí)現(xiàn)中放入<aop:scoped-proxy/>時(shí),它是工廠bean本身具有作用域,不是從getObject()中返回的對(duì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容