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.

當(dāng)你創(chuàng)建bean定義時,你創(chuàng)建了一個配方用于創(chuàng)建bean定義中定義的類的實例。bean定義是配方的想法是很重要的,因為這意味著對于一個類,你可以根據(jù)一個配方創(chuàng)建許多對象實例。

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定義中創(chuàng)建的。這種方法是強大且靈活的,你可以通過配置文件選擇你創(chuàng)建的對象的作用域,從而代替Java類級別對象的內(nèi)置作用域。定義的beans將部署成多種作用域中的一種:開箱即用,Spring框架支持六種作用域,如果你使用感知web的ApplicationContext,你只可以使用其中的五種作用域。

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

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

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定義只能創(chuàng)建一個對象實例。
prototype 單個bean定義可以創(chuàng)建任何數(shù)量的對象實例。
request 單個bean定義的創(chuàng)建實例的作用域為單個HTTP request的聲明周期;也就是說,每個HTTP request有它自己的根據(jù)bean定義創(chuàng)建的實例。只在感知Spring ApplicationContext的上下文中有效。
session 單個bean定義的創(chuàng)建實例的作用域為HTTP Session的生命周期. 只在感知Spring ApplicationContext的上下文中有效。
application 單個bean定義的創(chuàng)建實例的作用域為ServletContext的生命周期。 只在感知Spring ApplicationContext的上下文中有效。
websocket 單個bean定義的創(chuàng)建實例的作用域為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文檔。關(guān)于怎么注冊thread scope作用域或任何其它的定制作用域的介紹,請看『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只管理一個共享實例,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.

換言之,當(dāng)你定義一個bean定義時,它的作用域為單例,Spring IoC容器會根據(jù)bean定義創(chuàng)建一個確定的對象實例。這個單獨的實例存儲在單例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概念不同于《設(shè)計模式》書中定義的單例模式。設(shè)計模式中的單例是對對象的作用域進行硬編碼,為的是每個類加載器只能創(chuàng)建一個特定類的實例。Spring單例作用域最好的描述是每個容器每個類。這意味著如果你在單個的Spring容器中為一個特定的類定義了一個bean,Spring只會根據(jù)bean定義創(chuàng)建一個類的實例。在Spring中單例作用域是默認的作用域。為了在XML定義一個單例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部署采用原型作用域時,每次產(chǎn)生一個特定bean的請求時都會創(chuàng)建一個新的bean實例。也就是說,這個bean會注入到另一個bean中或你可以在容器中通過調(diào)用getBean()方法來請求它。通常,對于所有有狀態(tài)的beans使用原型作用域,對于無狀態(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ù)訪問對象(DAO)通常是不會配置為原型的,因為一個典型的DAO不會有任何會話狀態(tài);對于作者來說很容易重用單例圖的核心。

image

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

下面的例子在XML中定義一個原型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的完整生命周期:容器初始化、配置,另外組裝原型對象,并把它傳遞給客戶端,之后不再記錄原型實例。因此,雖然不管什么作用域初始化生命周期回調(diào)函數(shù)都會在所有對象上調(diào)用,但是在原型作用域的情況下,不會調(diào)用配置的銷毀生命周期回調(diào)函數(shù)??蛻舳舜a必須清理原型作用域的對象并釋放原型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生命周期的細節(jié),請看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時,要意識到依賴解析是在實例化時。因此如果你使用依賴注入將原型作用域的bean注入到單例作用域的bean中時,將會實例化一個新的原型bean并依賴注入到單例bean中。原型bean實例曾經(jīng)是唯一提供給單例作用域的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”.

假設(shè)你想在運行時讓單例作用域的bean重復(fù)的獲得原型作用域bean的新實例。你不能依賴注入原型作用域的bean到你的單例bean中,因為當(dāng)Spring容器實例化單例bean,解析并注入它的依賴時,注入只發(fā)生一次。如果你在運行時不止一次需要原型bean的實例,請看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實現(xiàn)(例如XmlWebApplicationContext),request,session,applicationwebsocket作用域是唯一可用的作用域。如果你通過正規(guī)的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,sessionapplicationwebsocket標(biāo)準的bean作用域,在你定義你的bean之前需要進行一些較小的初始化配置。(對于標(biāo)準作用域singletonprototype,初始化設(shè)置不需要的。)

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

怎樣完成這個初始化設(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,請求內(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之外處理請求(例如使用JSF或Struts時),你需要注冊org.springframework.web.context.request.RequestContextListener ServletRequestListener。對于Servlet 3.0+,能通過WebApplicationInitializer接口以編程方式處理。對于更早的容器,可以在應(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請求對象綁定到服務(wù)請求的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.

對于每一個HTTP請求,Spring容器通過使用loginAction定義創(chuàng)建一個新的LoginAction bean實例。也就是說,loginAction bean的作用域是在HTTP request級別的。你可以任意改變創(chuàng)建的實例的內(nèi)部狀態(tài),因為其它的根據(jù)loginAction bean定義創(chuàng)建的實例不會看到這些狀態(tài)的改變;它們對于每個單獨的請求都是獨有的。當(dā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ū)動的組件或Java配置時,@RequestScope注解能用來指定一個組件的作用域為request。

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

對于單個HTTP Session的生命周期,Spring容器通過userPreferences bean定義創(chuàng)建一個UserPreferences bean實例。換句話說,userPreferences bean的有效作用域是HTTP Session級別的。正如request作用域的beans一樣,你可以任意改變你想改變的創(chuàng)建的bean實例的內(nèi)部狀態(tài),知道其它的使用根據(jù)userPreferences bean定義創(chuàng)建的HTTP Session實例也不會看到這些內(nèi)部狀態(tài)的改變,因為它們對于每個單獨的HTTP Session都是獨有的。當(dāng)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.

當(dāng)使用注解驅(qū)動的組件或Java配置時,@SessionScope注解能用來指定一個組件的作用域為session。

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

對于整個web應(yīng)用而言,Spring容器根據(jù)appPreferences bean定義只創(chuàng)建一次AppPreferences bean的新實例。也就是說,appPreferences bean的作用域是ServletContext級別的,作為一個正規(guī)的ServletContext特性來存儲。這有點類似于Spring的單例bean,但在兩個方面是不同的:它對于每個ServletContext是單例的,而不是每個Spring ApplicationContext(在任何給定的web應(yīng)用中可能有幾個ApplicationContext),它是真正顯露的,因此作為一個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ū)動的組件或Java配置時,@ApplicationScope注解能用來指定一個組件的作用域為Application。

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

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/>,將通過中間代理的引用進行序列化,因此能通過反序列化重新獲得目標(biāo)的單例bean。

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

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

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

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"是重要的。

<?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)建這樣一個代理,你插入一個子元素<aop:scoped-proxy/>到具有作用域的bean定義中(看"選擇創(chuàng)建的代理類型"小節(jié)和38章,基于XML Schema的配置)。為什么bean定義的作用域為request,session和定制作用域級別需要<aop:scoped-proxy/>元素?讓我們檢查下面的單例bean定義,并將它與你需要定義的前面提到的作用域進行比較(注意下面的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中。這的突出點是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.

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

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é)作對象中時,你需要下面的,正確的,完整的配置:

<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)建代理時,默認情況下,創(chuàng)建一個基于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)用。在這個代理上不調(diào)用非公有方法;它們不能委托給實際作用域目標(biāo)對象。

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

<!-- 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)于選擇基于類或基于接口代理的更多細節(jié)信心,請看7.6小節(jié),"代理機制"。

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作用域機制是可擴展的;你可以定義你自己的作用域,甚至重新定義現(xiàn)有的作用域,雖然后者被認為是一種不好的實踐,你不能覆蓋內(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容器中,你需要實現(xiàn)org.springframework.beans.factory.config.Scope接口,這一節(jié)將描述這個接口。對于怎樣實現(xiàn)你自己作用域的想法,請看Spring框架本身提供的Scope實現(xiàn)和Scope文檔,它們解釋了你需要實現(xiàn)的方法的更多細節(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接口有四個方法,從作用域中取得對象,從作用域中移除對象,并且允許它們被銷毀。

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作用域?qū)崿F(xiàn)為例,返回具有session作用域的bean(如果它不存在,這個方法返回一個bean的新實例,然后綁定到session中準備將來引用)。

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作用域?qū)崿F(xiàn)為例,從潛在的session中移除session作用域的bean。對象應(yīng)該被返回,但如果沒有找到指定名字的對象會返回空。

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.

下面的方法是注冊當(dāng)作用域銷毀時或當(dāng)作用域中的指定對象銷毀時,作用域應(yīng)該執(zhí)行的回調(diào)函數(shù)。銷毀回調(diào)函數(shù)的更多信息請看文檔或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.

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

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實現(xiàn)之后,你需要讓Spring容器感知到你的新作用域。下面是在Spring容器中注冊一個新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.

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

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

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

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是一樣的。

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實現(xiàn)后,你不會受限于作用域的程序注冊。你也可以聲明式的進行Scope注冊,使用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實現(xiàn)中放入<aop:scoped-proxy/>時,它是工廠bean本身具有作用域,不是從getObject()中返回的對象。

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

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

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