Spring bean作用域
先看看spring的bean作用域有幾種,它們之間分別有啥不同。Spring bean作用域有以下5個:
- singleton:單例模式,默認,當spring創建applicationContext容器的時候,Spring會欲初始化所有的該作用域實例,加上lazy-init就可以避免預處理;
- prototype:原型模式,每次通過getBean獲取該bean就會新產生一個實例,創建后Spring將不再對其管理;
******* 下面是在web項目下才用到的 *******
- request:搞web的大家都應該明白request的域了吧,就是每次請求都新產生一個實例,和prototype的不同之處就是創建后接下來的管理,Spring依然在監聽;
- session:每次會話,同上;
- global session:全局的web域,類似於servlet中的application。
好了,上面都說了Spring的controller默認是單例,那很自然就是singleton了。
再通過一個例子看看單例會不會有如下問題——類中定義的非靜態變量線程安全問題。為什么Spring要默認是單例呢?原因有二:
1、為了性能。這個不用廢話了,單例不用每次都new,當然快了。
2、不需要多例。不需要多實例會讓很多人迷惑,因為Spring MVC官方也沒明確說不可以多例。
我這里說不需要的原因是看開發者怎么用了,開發者如果在controller中定義很多的屬性,那么單例肯定會出現競爭訪問了。因此,只要controller中不定義屬性,那么單例完全是安全的。下面給個例子說明下:
import org.springframework.context.annotation.Scope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/scopeTest") // @Scope("prototype")
public class ScopeTestController { private int num = 0; //非靜態成員變量
@RequestMapping("/testScope") public void testScope() { System.out.println("testScope --> " + ++num); } @RequestMapping("/testScope2") public void testScope2() { System.out.println("testScope2 --> " + ++num); } }
依次請求testScope()和testScope2()函數,執行結果是
testScope --> 1 testScope2 --> 2
改為多例,即去掉@Scope("prototype")前面的注釋,則執行結果是
testScope --> 1 testScope2 --> 1
相信大家不難發現 :單例是線程不安全的,會導致屬性重復使用。如果把num定義為類變量,無論是否使用多例模式,都會復用類變量,感興趣的小伙伴可以驗證一下。解決方案:
1、不要在controller中定義成員變量。
2、萬一必須要定義一個非靜態成員變量,則通過注解@Scope(“prototype”)將其設置為多例模式。
3、在controller中使用ThreadLocal變量。
Reference
https://www.cnblogs.com/zxf330301/articles/6105127.html