Spring裝配Bean(六)Bean的作用域

z1340954953發表於2018-06-01

預設情況下,Spring應用上下文中所有bean都是作為以單例的形式建立的。也就是說,不管給定的一個bean被注入到其他bean多少次,每次所注入的都是同一個例項

在大多數情況下,單例bean時很理想的方案。有時候存在,所使用的類是易變的,對這些類的bean進行重用可能就不安全,例如購物車bean如果是單例的話,每個使用者都向一個購物車中新增商品,這就有問題了

Spring定義了多種作用域,可以基於這些作用域建立bean,包括:

1> 單例(Singleton):在整個應用中,只建立bean的一個例項

2> 原型(Prototype):每次注入或者通過Spring應用上下文獲取的時候,都會去建立一個新的Bean

3> 會話(Session):在Web應用中,為每個會話建立一個Bean例項。

4> 請求(Request):在Web應用中,為每個請求建立一個Bean例項

* JavaConfig中設定作用域

@Scope註解和@Component或@Bean一起使用

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public CDPlayer cdplayer(){
    return new CDPlayer(this.cd);
}
@Component
@Qualifier("compactDisc2")
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SgtPeppers implements CompactDisc {
}

這裡,使用ConfigurableBeanFactory類的SCOPE_PROTOTYPE常量設定了原型作用域,當然也可以使用@Scope("prototype"),但是SCOPE_PROTOTYPE常量更加安全並且不易出錯。

* XML中配置Bean的作用域

 <bean id="compactDisc1" class="com.erong.service.SgtPeppers" scope="prototype">
   		<constructor-arg name="title" value="遙遠的ren"></constructor-arg>
   		<constructor-arg name="artist" value="big ben..."></constructor-arg>
   </bean>

* 使用會話和請求作用域

在Web應用中,如果能夠例項化在會話和請求範圍內共享的Bean,將是很有價值的事情。

例如,在典型的電子商務應用中,可能會有一個Bean代表使用者的購物車。如果購物車是單例的,那麼就會導致多個使用者向同一個購物車中新增商品,如果購物車是原型的,在一個位置設定了,購物車的屬性(商品名稱,種類..),在另一個地方通過注入使用的時候,將拿不到剛才那個Bean,而是一個新建立的Bean,這就存在問題了。

* JavaConfig中宣告作用域代理

就購物車bean來說,會話作用域最合適

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

}

我們將value設定成WebApplicationContext中的SCOPE_SESSION常量。將告訴Spring為Web應用中的每個會話建立Bean。

注意的是,@Scope同時還有一個proxyMode屬性,被設定為了ScopedProxyMode.INTERFACES. 解決了將會話或請求作用域的bean注入到單例bean中遇到的問題.

現在將ShoppingCart注入到單例StoreService bean的setter方法中,如下所示:

@Component
@Qualifier("storeService")
public class StoreService {
	private ShoppingCart sc ;
	@Autowired
	@Qualifier("sc")
	public void setShoppingCart(ShoppingCart sc){
		this.sc = sc;
	}
}

StoreService是單例的,建立這個Bean的時候,Spring上下文回去嘗試自動注入ShoppingCart這個Bean,但是隻要是使用者還沒有訪問系統,這個bean就不會建立。

另外,對於每個使用者,都會去建立ShoppingCart這個Bean,也就是StoreService注入的Bean,不是唯一的,我們希望的是注入的購物車Bean,剛好是當前會話的那個。

Spring實際上不將將真正的ShoppingCart Bean注入到StoreService,而是注入它的代理,到具體使用到ShoppingCart Bean才會去,進行懶解析找到實際的Bean。

proxyMode屬性設定為ScopedProxyMode.INTERFACES,這表明這個代理將要實現ShoppingCart介面,並將呼叫委託給實現bean.

如果ShoppingCart是介面而不是類,這是可以的(也是最為理想的代理模式),但如果shoppingCart是一個具體的類的話,Spring 無法建立基於介面的代理了,此時,必須使用CGLIB生成基於類的代理。將proxyMode屬性設定為ScopedProxyMode.

TARGET_CALSS

* 在XML中宣告作用域代理

<bean id="helloWorld" class="com.erong.service.HelloWorld" scope="session">
   	<aop:scoped-proxy proxy-target-class="false"/>
   </bean>

預設是採用cglib生成代理物件,如果將proxy-target-class="false" 將表示基於介面生成代理物件



相關文章