?
Dokumen ini menggunakan Manual laman web PHP Cina Lepaskan
創(chuàng)建一個(gè)bean定義,其實(shí)質(zhì)是用該bean定義對(duì)應(yīng)的類來(lái)創(chuàng)建真正實(shí)例的“配方(recipe)”。把bean定義看成一個(gè)配方很有意義,它與class很類似,只根據(jù)一張“處方”就可以創(chuàng)建多個(gè)實(shí)例。
你不僅可以控制注入到對(duì)象中的各種依賴和配置值,還可以控制該對(duì)象的作用域。這樣你可以靈活選擇所建對(duì)象的作用域,而不必在Java Class級(jí)定義作用域。Spring Framework支持五種作用域(其中有三種只能用在基于web的Spring ApplicationContext
)。
內(nèi)置支持的作用域分列如下:
表?3.4.?Bean作用域
作用域 | 描述 |
---|---|
singleton |
在每個(gè)Spring IoC容器中一個(gè)bean定義對(duì)應(yīng)一個(gè)對(duì)象實(shí)例。 |
prototype |
一個(gè)bean定義對(duì)應(yīng)多個(gè)對(duì)象實(shí)例。 |
request |
在一次HTTP請(qǐng)求中,一個(gè)bean定義對(duì)應(yīng)一個(gè)實(shí)例;即每次HTTP請(qǐng)求將會(huì)有各自的bean實(shí)例,
它們依據(jù)某個(gè)bean定義創(chuàng)建而成。該作用域僅在基于web的Spring
|
session |
在一個(gè)HTTP |
global session |
在一個(gè)全局的HTTP |
當(dāng)一個(gè)bean的作用域?yàn)閟ingleton, 那么Spring IoC容器中只會(huì)存在一個(gè)共享的bean實(shí)例,并且所有對(duì)bean的請(qǐng)求,只要id與該bean定義相匹配,則只會(huì)返回bean的同一實(shí)例。
換言之,當(dāng)把一個(gè)bean定義設(shè)置為singlton作用域時(shí),Spring IoC容器只會(huì)創(chuàng)建該bean定義的唯一實(shí)例。這個(gè)單一實(shí)例會(huì)被存儲(chǔ)到單例緩存(singleton cache)中,并且所有針對(duì)該bean的后續(xù)請(qǐng)求和引用都將返回被緩存的對(duì)象實(shí)例。
請(qǐng)注意Spring的singleton bean概念與“四人幫”(GoF)模式一書(shū)中定義的Singleton模式是完全不同的。經(jīng)典的GoF Singleton模式中所謂的對(duì)象范圍是指在每一個(gè)ClassLoader
中指定class創(chuàng)建的實(shí)例有且僅有一個(gè)。把Spring的singleton作用域描述成一個(gè)container
對(duì)應(yīng)一個(gè)bean實(shí)例最為貼切。亦即,假如在單個(gè)Spring容器內(nèi)定義了某個(gè)指定class的bean,那么Spring容器將會(huì)創(chuàng)建一個(gè)且僅有一個(gè)由該bean定義指定的類實(shí)例。Singleton作用域是Spring中的缺省作用域。要在XML中將bean定義成singleton,可以這樣配置:
<bean id="accountService" class="com.foo.DefaultAccountService"/> <!-- the following is equivalent, though redundant (singleton scope is the default); usingspring-beans-2.0.dtd
--> <bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/> <!-- the following is equivalent and preserved for backward compatibility inspring-beans.dtd
--> <bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>
Prototype作用域的bean會(huì)導(dǎo)致在每次對(duì)該bean請(qǐng)求(將其注入到另一個(gè)bean中,或者以程序的方式調(diào)用容器的getBean()
方法)時(shí)都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。根據(jù)經(jīng)驗(yàn),對(duì)有狀態(tài)的bean應(yīng)該使用prototype作用域,而對(duì)無(wú)狀態(tài)的bean則應(yīng)該使用singleton作用域。
下圖演示了Spring的prototype作用域。請(qǐng)注意,通常情況下,DAO不會(huì)被配置成prototype,因?yàn)镈AO通常不會(huì)持有任何會(huì)話狀態(tài),因此應(yīng)該使用singleton作用域。
要在XML中將bean定義成prototype,可以這樣配置:
<!-- usingspring-beans-2.0.dtd
--> <bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/> <!-- the following is equivalent and preserved for backward compatibility inspring-beans.dtd
--> <bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>
對(duì)于prototype作用域的bean,有一點(diǎn)非常重要,那就是Spring不能對(duì)一個(gè)prototype bean的整個(gè)生命周期負(fù)責(zé):容器在初始化、配置、裝飾或者是裝配完一個(gè)prototype實(shí)例后,將它交給客戶端,隨后就對(duì)該prototype實(shí)例不聞不問(wèn)了。不管何種作用域,容器都會(huì)調(diào)用所有對(duì)象的初始化生命周期回調(diào)方法。但對(duì)prototype而言,任何配置好的析構(gòu)生命周期回調(diào)方法都將不會(huì)被調(diào)用。清除prototype作用域的對(duì)象并釋放任何prototype bean所持有的昂貴資源,都是客戶端代碼的職責(zé)。(讓Spring容器釋放被prototype作用域bean占用資源的一種可行方式是,通過(guò)使用bean的后置處理器,該處理器持有要被清除的bean的引用。)
談及prototype作用域的bean時(shí),在某些方面你可以將Spring容器的角色看作是Java new
操作的替代者。任何遲于該時(shí)間點(diǎn)的生命周期事宜都得交由客戶端來(lái)處理。(在第?3.5.1?節(jié) “生命周期回調(diào)”一節(jié)中會(huì)進(jìn)一步講述Spring容器中的bean生命周期。)
當(dāng)使用依賴于prototype bean的singleton-scoped bean時(shí),請(qǐng)注意依賴是在實(shí)例化時(shí)處理的。這也就是說(shuō),如果要把一個(gè)prototype-scoped bean注入到singleton-scoped bean,實(shí)際上只是實(shí)例化一個(gè)新的prototype bean注入到 singleton bean...但這是全部。這種情況下,singleton-scoped bean獲得的prototype實(shí)例是唯一的。
然而,你可能需要在運(yùn)行期讓singleton-scoped bean每次都獲得prototype-scoped bean的新實(shí)例。在這種情況下,只將prototype-scoped bean注入到你的singleton bean中是沒(méi)有用的,因?yàn)檎缟衔乃f(shuō)的,僅僅在當(dāng)Spring容器實(shí)例化singleton bean并且處理注入的依賴時(shí),生成唯一實(shí)例。如果你需要在運(yùn)行期一次又一次的生成(prototype) bean的新實(shí)例,你可以參考第?3.3.7?節(jié) “方法注入”
如果你在bean定義文件中引用'spring-beans.dtd'
DTD,
要顯式說(shuō)明bean的生命周期作用域你必須使用"singleton
"屬性(記住singleton生命周期作用域是默認(rèn)的)。
如果引用的是'spring-beans-2.0.dtd'
DTD或者是Spring 2.0 XSD schema,
那么需要使用"scope
"屬性(因?yàn)?singleton
"屬性被刪除了,
新的DTD和XSD文件使用"scope
"屬性)
簡(jiǎn)單地說(shuō),如果你用"singleton
"屬性那么就必須在那個(gè)文件里
引用'spring-beans.dtd'
DTD。
如果你用"scope
"屬性那么必須 在那個(gè)文件里引用'spring-beans-2.0.dtd'
DTD 或'spring-beans-2.0.xsd'
XSD。
其他作用域,即request
、session
以及global session
僅在基于web的應(yīng)用中使用(不必關(guān)心你所采用的是什么web應(yīng)用框架)。
下面介紹的作用域僅僅在使用基于web的Spring ApplicationContext
實(shí)現(xiàn)(如XmlWebApplicationContext
)時(shí)有用。
如果在普通的Spring IoC容器中,比如像XmlBeanFactory
或ClassPathXmlApplicationContext
,
嘗試使用這些作用域,你將會(huì)得到一個(gè)IllegalStateException
異常(未知的bean作用域)。
要使用request
、session
和
global session
作用域的bean(即具有web作用域的bean),
在開(kāi)始設(shè)置bean定義之前,還要做少量的初始配置。請(qǐng)注意,假如你只想要“常規(guī)的”作用域,(singleton和prototype),就不需要這一額外的設(shè)置。
在目前的情況下,根據(jù)你的特定servlet環(huán)境,有多種方法來(lái)完成這一初始設(shè)置...
如果你用Spring Web MVC,即用SpringDispatcherServlet
或DispatcherPortlet
來(lái)處理請(qǐng)求,則不需要做特別的配置:DispatcherServlet
和
DispatcherPortlet
已經(jīng)處理了所有有關(guān)的狀態(tài)
當(dāng)使用了Spring's DispatcherServlet以外的Servlet 2.4及以上的Web容器時(shí)(如使用JSF或Struts),你需要在Web應(yīng)用的'web.xml'
文件中增加 javax.servlet.ServletRequestListener
定義
<web-app> ... <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> ... </web-app>
如果你用的是早期版本的web容器(Servlet 2.4以前的版本),那么你要使用一個(gè)javax.servlet.Filter
的實(shí)現(xiàn)。請(qǐng)看下面的web.xml配置片段:
<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>*Context.xml(WEB-INF文件夾及子文件夾下的以"Context.xml"結(jié)尾的文件)。
ContextLoaderServlet
同ContextLoaderListener
一樣使用'contextConfigLocation'
參數(shù)。