1. 程式人生 > >Spring框架Bean作用域

Spring框架Bean作用域

Spring框架現有的Bean作用域

( 1 ) singleton :預設的作用域,僅為每個 Bean 物件建立一個例項。

( 2 ) prototype :可以根據需要為每個 Bean 物件建立多個例項。

( 3 ) request :為每個 HTTP 請求建立它自有的一個 Bean 例項,僅在 Web 相關的 ApplicationContext 中生效。

( 4 ) session :為每個 HTTP 會話建立一個例項,僅在 Web 相關的 ApplicationContext 中生效。

( 5 ) global session :為每個全域性的 HTTP 會話建立一個例項。一般僅在 porlet上下文中使用生效。同時僅在 Web 相關的 ApplicationContext 中生效。

( 6 ) application :為每個 ServletContext 建立一個例項。僅在 Web 相關的 ApplicationContext 中生效。

1 singleton 作用域

當一個bean的作用域設定為singleton, 那麼Spring IOC容器中只會存在一個共享的bean例項,並且所有對bean的請求,只要id與該bean定義相匹配,則只會返回bean的同一例項。換言之,當把一個bean定義設定為singleton作用域時,Spring IOC容器只會建立該bean定義的唯一例項。這個單一例項會被儲存到單例快取(singleton cache)中,並且所有針對該bean的後續請求和引用都將返回被快取的物件例項,這裡要注意的是singleton作用域和GOF設計模式中的單例是完全不同的,
GoF 描述的是,指定的類在每個 ClassLoader 中僅會建立一個例項,並且是通過硬編碼來完成的。而 Spring 中的單例最好解釋為每個容器僅有 Bean 的一個例項。這就意味著,如果在 Spring 容器中定義一個 Bean ,容器將會根據定義為 Bean建立僅僅一個例項。在 Spring 中, singleton 作用域是預設的作用域。

2 prototype 作用域

非單例的 prototype 作用域,在每次請求一個指定 Bean 的時候都會建立一個新的例項。通常,對於具有狀態的 Bean 使用 prototype ,對於無狀態的 Bean 使用 singleton。

下面的例子將一個 Bean 定義為 prototype :

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

與其它作用域相比, Spring 不會管理一個 prototype 作用域 Bean 的完整生命週期。容器例項化、配置和組裝一個 prototype 例項,然後轉交給客戶端,不再繼續跟蹤那個 prototype 例項。因此,處於初始化生命週期的回撥方法在所有作用域下都會被呼叫,而在 prototype 作用域下,析構生命週期回撥不會被呼叫。客戶端程式碼必須清理 prototype 作用域物件,釋放物件所持有的昂貴的資源。

當單例 Bean 依賴 prototype 型別的 Bean 時,注意這種依賴是在例項化的時候解析的,一個新的 prototype 型別的 Bean 會被例項化然後注入到單例 Bean 中,因此這個prototype 型別的 Bean 對於這個單例 Bean 而言只有一個,而不是我們所認為的為每次請求單例 Bean 會建立一個新的 prototype 型別的 Bean 。如果要為每次請求單例 Bean 建立一個新的 prototype 型別的 Bean ,就不能這樣做了,因為注入只會發生一次,即在 Spring 容器在例項化單例和解析依賴的時候。若需要這樣的功能,可以參考方法注入 。

3 request, session, global session 和 application 作用域

request, session 和 global session 作用域僅在使用 Web 相關的 ApplicationContext實現(比如 XmlWebApplicationContext )時才會有用。如果使用平常的 Spring IoC容器(比如 ClassPathXmlApplicationContext ),將會丟擲 IllegalStateException 異常。

為了支援 request, session 和 global session 作用域,在定義 Bean 之前需要做一些必要的配置。具體如何完成這樣的配置決定於特定的 Servlet 環境。如果使用的是 Spring Web MVC ,通過 DispatcherServlet 或者 DispatcherPortlet 來執行請求,不需要做任何設定, DispatcherServlet 或者 DispatcherPortlet 已經暴露了所有相關的狀態。

如果使用的是 Servlet2.5 的 Web 容器,且使用的是 DispatcherServlet 之外的 Servlet (比如 Struts )來執行請求,那麼就需要註冊 org.springframework.web.context.request.RequestContextListener ServletRequestListener 。對於 Servlet3.0+ ,可以通過 WebApplicationInitializer 介面編碼實現這樣的功能。

3.1 request 作用域

請看如下示例:

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

Spring 容器為每個 HTTP 請求建立一個新的 loginAction 例項,這個例項的狀態可以根據需要進行修改,而不會影響到其它請求中建立的例項。當請求執行完畢時,那麼這個例項就會被遺棄。

3.2 session 作用域

請看如下示例:

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

Spring 容器會為每個會話建立一個 userPreferences 例項。跟 request 作用域類似,例項的狀態可以根據需要進行修改,不會影響到其它會話中建立的例項。

3.3 global session 作用域

global session 作用域跟 session 作用域類似,僅應用於基於 portlet 的上下文。

3.4 application 作用域

請看如下示例:

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

Spring 會為整個 Web 應用建立一個 appPreferences 例項,作為 ServletContext 的一個屬性儲存著。它跟 Spring 的單例 Bean 有點像,但也有不同,它在每個 ServletContext 中是單例的,而非 Spring 的 ApplicationContext 。

3.5 作用域 Bean 依賴

如果想把一個 HTTP request 作用域的 Bean 注入到另一個長週期作用域的 Bean中,就需要選擇注入一個 AOP 代理來替換這個 request 作用域 Bean 。也就是,需要注入一個代理物件,這個代理物件暴露了 request 作用域 Bean 的公共介面,可以查詢到真實的目標物件,然後將方法呼叫委託到真實的物件上面。比如在單例 Bean 中注入一個會話 Bean ,若不使用代理,那麼在會話 Bean 失效之後,單例 Bean 將不能再獲取到會話 Bean 。若使用代理,那麼代理物件將從作用域機制中找到對應的 Bean 。

示例如下:

<?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">
    <!—一個HTTP 會話作用域的Bean 作為代理暴露出去-->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
        <!—指示容器代理這個Bean -->
        <aop:scoped-proxy/>
    </bean>
    <!—一個單例Bean注入一個代理Bean -->
    <bean id="userService" class="com.foo.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>