Spring 學(xué)習(xí)筆記(四)bean的作用域

在默認(rèn)情況下,Spring應(yīng)用上下文中所有bean都是作為以單例(singleton)的形式創(chuàng)建的。
在大多數(shù)情況下,單例bean是很理想的方案。初始化和垃圾回收對(duì)象實(shí)所帶來(lái)的成本只留給一些小規(guī)模任務(wù),在這些任務(wù)中,讓對(duì)象保持無(wú)狀態(tài)且在應(yīng)用中反復(fù)重用這些對(duì)象可能并不合理。
有時(shí)候,可能會(huì)發(fā)現(xiàn),你所使用的類(lèi)是易變的(mutable),它們會(huì)保持一些狀態(tài),因此重用是不安全的。在這種情況下,將class聲明為單例的bean就不是什么好主意了,因?yàn)閷?duì)象會(huì)被污染,稍后重用的時(shí)候會(huì)出現(xiàn)意想不到的問(wèn)題。

Spring定義了多種作用域,可以基于這些作用域創(chuàng)建bean,包括:
-單例(Singleton):在整個(gè)應(yīng)用中,只創(chuàng)建bean的一個(gè)實(shí)例。
-原型(Prototype):每次注入或者通過(guò)Spring應(yīng)用上下文獲取的時(shí)候,都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。
-會(huì)話(Session):在Web應(yīng)用中,為每個(gè)會(huì)話創(chuàng)建一個(gè)bean實(shí)例。
-請(qǐng)求(Rquest):在Web應(yīng)用中,為每個(gè)請(qǐng)求創(chuàng)建一個(gè)bean實(shí)例。

如果選擇其他的作用域,要使用@Scope注解,它可以與@Component或@Bean一起使用。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad{...}

如果你想在Java配置中將Notepad聲明為原型bean,那么可以組合使用@Scope和@Bean來(lái)指定所需的作用域:

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad notepad(){
    return new Notepad();
}

使用 xml

<bean id="notepad" class="com.myapp.Notepad" scope="prototype"/>

使用會(huì)話和請(qǐng)求作用域

在Web應(yīng)用中,如果能夠?qū)嵗跁?huì)話和請(qǐng)求范圍內(nèi)共享的bean,那將是非常有價(jià)值的事情。例如,在典型的電子商務(wù)應(yīng)用中,可能會(huì)有一個(gè)bean代表用戶的購(gòu)物車(chē)。如果購(gòu)物車(chē)是單例的話,那么將會(huì)導(dǎo)致所有的用戶都會(huì)向同一個(gè)購(gòu)物車(chē)中添加商品。另一方面,如果購(gòu)物車(chē)是原型作用域的,那么在應(yīng)用中某一個(gè)地方往購(gòu)物車(chē)中添加商品,在應(yīng)用的另外一個(gè)地方可能就不可用了,因?yàn)樵谶@里注入的是另外一個(gè)原型作用域的購(gòu)物車(chē)。
就購(gòu)物車(chē)bean來(lái)說(shuō),會(huì)話作用域是最為合適的,因?yàn)樗c給定的用戶關(guān)聯(lián)性最大。要指定會(huì)話作用域,我們可以使用@Scope注解,它的使用方式與指定原型作用域是相同的:

@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() {...}

這里,我們將value設(shè)置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。這會(huì)告訴Spring為Web應(yīng)用中的每個(gè)會(huì)話創(chuàng)建一個(gè)ShoppingCart。這會(huì)創(chuàng)建多個(gè)ShoppingCart bean的實(shí)例,但是對(duì)于給定的會(huì)話只會(huì)創(chuàng)建一個(gè)實(shí)例,在當(dāng)前會(huì)話相關(guān)的操作中,這個(gè)bean實(shí)際上相當(dāng)于單例的。

要注意的是,@Scope同時(shí)還有一個(gè)proxyMode屬性,它被設(shè)置成了ScopedProxyMode.INTERFACES。這個(gè)屬性解決了將會(huì)話或請(qǐng)求作用的bean注入到單例bean中所遇到的問(wèn)題。在描述proxyMode屬性之前,我們先來(lái)看一下proxyMode所解決問(wèn)題的場(chǎng)景。假設(shè)我們要將ShoppingCart bean注入到單例StoreService bean的Setter方法中,如下所示:

@Component
public class StoreService{
    @Autowired
    public void setShoppingCart(ShoppingCart shoppingCart){
        this.shoppingCart = shoppingCart;
    }
}

因?yàn)镾toreService是一個(gè)單例的bean,會(huì)在Spring應(yīng)用上下文加載的時(shí)候創(chuàng)建。當(dāng)它創(chuàng)建的時(shí)候,Spring會(huì)試圖將ShoppingCart bean注入到setShoppingCart()方法中。但是ShoppingCart bean是會(huì)話作用域的,此時(shí)并不存在。直到某個(gè)用戶進(jìn)入系統(tǒng),創(chuàng)建了會(huì)話之后,才會(huì)出現(xiàn)ShoppingCart實(shí)例。

另外,系統(tǒng)中將會(huì)有多個(gè)ShoppingCart實(shí)例:每個(gè)用戶一個(gè)。我們并不想讓Spring注入某個(gè)固定的ShoppingCart實(shí)例到StoreService中。我們希望的是當(dāng)StoreService處理購(gòu)物車(chē)功能時(shí),它所使用的ShoppingCart實(shí)例恰好是當(dāng)前會(huì)話所對(duì)應(yīng)的那一個(gè)。

Spring并不會(huì)將實(shí)際的ShoppingCart bean注入到StoreService中,Spring會(huì)注入一個(gè)到ShoppingCart bean的代理,如圖3.1所示。這個(gè)代理會(huì)暴露與ShoppingCart相同的方法,所以StoreService會(huì)認(rèn)為它就是一個(gè)購(gòu)物車(chē)。但是,當(dāng)StoreService調(diào)用ShoppingCart的方法時(shí),代理會(huì)對(duì)其進(jìn)行懶解析并將調(diào)用委托給會(huì)話作用域內(nèi)真正的ShoppingCart bean。

現(xiàn)在,我們帶著對(duì)這個(gè)作用域的理解,討論一下proxyMode屬性。如配置所示,proxyMode屬性被設(shè)置成了ScopedProxyMode.INTERFACES,表明這個(gè)代理要實(shí)現(xiàn)ShoppingCart接口,并將調(diào)用委托給實(shí)現(xiàn)bean。

如果ShoppingCart是接口而不是類(lèi)的話,這是可以的(也是最為理想的代理模式)。但如果ShoppingCart是一個(gè)具體的類(lèi)的話,Spring就沒(méi)有辦法創(chuàng)建基于接口的代理了。此時(shí),它必須使用CGLib來(lái)生成基于類(lèi)的代理。所以,如果bean類(lèi)型是具體類(lèi)的話,我們必須要將proxyMode屬性設(shè)置為ScopedProxyMode.TARGET_CLASS,以此來(lái)表明要以生成目標(biāo)類(lèi)擴(kuò)展的方式創(chuàng)建代理。

image.png

如果感興趣,請(qǐng)關(guān)注我的微信公眾號(hào):

Capture.PNG
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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