Spring-bean的作用域


在大多數情況下,單例bean是很理想的方案。初始化和垃圾回收對象實例所帶來的的成本只留給一些小規模任務,在這些任務中,讓對象保持無狀態並且在應用中反復重用這些對象可能並不合理。在這種情況下,將class聲明為單例的bean會被污染,稍后重用的時候會出現意想不到的問題。

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

  • 單例(singleton):在整個應用中,只創建bean的一個實例。
  • 原型(prototype):每次注入或者通過Spring應用上下文獲取的時候,都會創建一個新的實例。
  • 會話(session):在Web應用中,為每個會話創建一個bean實例。
  • 請求(request):在Web應用中,為每個請求創建一個bean實例。

單例是默認的作用域,如果選擇其他作用域,要使用@Scope注解,它可以與@Component或@Bean一起使用:

1 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
2 import org.springframework.context.annotation.Scope;
3 
4 @Component
5 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
6 public class Notepad{...}

當然也可以在@Bean下使用,參數可以直接寫字符串prototype,singleton等,但並沒有上述方式安全。

在xml模式中,可以在bean的屬性中添加scope="prototype":

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

使用會話和請求作用域

使用會話作用域的bean最合適的莫過於購物車了,一個用戶用一個購物車,而不是共用,或者因商品種類不同而創造無用的購物車。因為它與給定的用戶關聯性最大。示例如下:

 1 package soundSystem;
 2 
 3 import org.springframework.context.annotation.Scope;
 4 import org.springframework.context.annotation.ScopedProxyMode;
 5 import org.springframework.stereotype.Component;
 6 import org.springframework.web.context.WebApplicationContext;
 7 
 8 @Component
 9 @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES)
10 public class JDShoppingCart implements ShoppingCart{
11     public ShoppingCart getJDShoppingCart(){
12         return new JDShoppingCart();
13     }
14 }

 這里,我們將value設置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。表明為Web應用中的每個會話創建一個ShoppingCart。這會創建多個ShoppingCart實例,但對於給定的會話,只創建一個實例。也就是說,在當前會話相關的操作中,ShoppingCart是單例的。

要注意的是,還有一個代理模式的屬性,它被設置成ScopedProxyMode.INTERFACES。這個屬性解決了講會話或請求作用域的bean注入到單例bean中所遇到的問題。在描述proxyMode屬性之前,我們先來看看proxyMode所解決問題的場景。假如要將ShoppingCart bean注入到單例StoreService bean的Setter方法中:

 1 package soundSystem;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Component;
 5 
 6 @Component
 7 public class StoreService {
 8 private ShoppingCart shoppingCart;
 9 
10 @Autowired
11 public void setShoppingCart(ShoppingCart shoppingCart) {
12     this.shoppingCart = shoppingCart;
13 }
14 }

 因為StoreService是一個單例的bean,會在Spring應用上下文加載的時候創建。當它創建的時候,Spring會試圖將ShoppingCart bean注入到setter方法中,但是ShoppingCart bean是會話作用域的,此時並不存在,直到某個用戶進入系統,創建了會話之后,才會出現ShoppingCart實例。

另外,系統中將會有多個ShoppingCart實例:每個用戶一個。我們並不想讓Spring注入某個固定的ShoppingCart實例到StoreService中。我們希望的是當StoreService處理購物車功能時,它所使用的ShoppingCart實例恰好是當前會話所對應的那個。

所以Spring並不會將實際的ShoppingCart bean注入到StoreService中,Spring會注入到一個ShoppingCart bean的代理,這個代理會暴露與ShoppingCart相同的方法,所以StoreService會認為它是一個購物車。但是當StoreService調用ShoppingCart的方法時,代理會對其進行懶解析並將調用委托給會話作用域內真正的ShoppingCart bean。

現在,proxyMode被設置成ScopedProxyMode.INTERFACE常量,表明這個代理要實現ShoppingCart接口,並將調用委托給實現bean。如果需要實現類,則設置為ScopedProxyMode.TARGET_CLASS即可。

xml模式下配置代理模式

1 <bean id="jDShoppingCart" class="soundSystem.JDShoppingCart" scope="session">
2     <aop:scoped-proxy/>
3 </bean>

 

也可以將proxy-target-class設置為false,從而要求它生成基於接口的代理:

1         <bean id="jDShoppingCart" class="soundSystem.JDShoppingCart" scope="session">
2             <aop:scoped-proxy proxy-target-class="false"/>
3         </bean>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM