Spring Bean Scope 有狀態(tài)的Bean與無狀態(tài)的Bean
有狀態(tài)會(huì)話bean:每個(gè)用戶有自己特有的一個(gè)實(shí)例,在用戶的生存期內(nèi),bean保持了用戶的信息,即“有狀態(tài)”;一旦用戶滅亡(調(diào)用結(jié)束或?qū)嵗Y(jié)束),bean的生命期也告結(jié)束。即每個(gè)用戶最初都會(huì)得到一個(gè)初始的bean。
無狀態(tài)會(huì)話bean:bean一旦實(shí)例化就被加進(jìn)會(huì)話池中,各個(gè)用戶都可以共用。即使用戶已經(jīng)消亡,bean 的生命期也不一定結(jié)束,它可能依然存在于會(huì)話池中,供其他用戶調(diào)用。由于沒有特定的用戶,那么也就不能保持某一用戶的狀態(tài),所以叫無狀態(tài)bean。但無狀態(tài)會(huì)話bean 并非沒有狀態(tài),如果它有自己的屬性(變量),那么這些變量就會(huì)受到所有調(diào)用它的用戶的影響,這是在實(shí)際應(yīng)用中必須注意的。
在Spring的Bean配置中,存在這樣兩種情況:
<bean id="testManager" class="com.sw.TestManagerImpl" scope="singleton" /> ? ?? ?<bean id="testManager" class="com.sw.TestManagerImpl" scope="prototype" /> ?
當(dāng)然,scope
的值不止這兩種,還包括了request
,session
等。但用的最多的還是singleton單態(tài),prototype多態(tài)。
singleton
表示該bean全局只有一個(gè)實(shí)例,Spring中bean的scope默認(rèn)也是singleton.
prototype
表示該bean在每次被注入的時(shí)候,都要重新創(chuàng)建一個(gè)實(shí)例,這種情況適用于有狀態(tài)的Bean.
對(duì)于SSH架構(gòu)的系統(tǒng),很少關(guān)心這方面,因?yàn)槲覀冇玫降囊话愣际莝ingleton. Bean的注入由Spring管理。
對(duì)于有狀態(tài)的Bean呢?
下面是一個(gè)有狀態(tài)的Bean:
package com.sw; ? ?? public class TestManagerImpl implements TestManager{ ? ? ? private User user; ? ? ?? ? ? public void deleteUser(User e) throws Exception { ? ? ? ? ? user = e ; ? ? ? ? ? //1 ? ? ? ? ? prepareData(e); ? ? ? } ? ?? ? ? public void prepareData(User e) throws Exception { ? ? ? ? ? user = getUserByID(e.getId()); ? ? ? ? ? ?//2 ? ? ? ? ? ..... ? ? ? ? ? //使用user.getId(); ? ? ? ? ? ? ? ? ? ? ? //3 ? ? ? ? ? ..... ? ? ? ? ? ..... ? ? ? } ? ?? } ?
如果該Bean配置為singleton,會(huì)出現(xiàn)什么樣的狀況呢?
如果有2個(gè)用戶訪問,都調(diào)用到了該Bean.
假定為user1,user2
當(dāng)user1 調(diào)用到程序中的1步驟的時(shí)候,該Bean的私有變量user被付值為user1
當(dāng)user1的程序走到2步驟的時(shí)候,該Bean的私有變量user被重新付值為user1_create
理想的狀況,當(dāng)user1走到3步驟的時(shí)候,私有變量user應(yīng)該為user1_create;
但如果在user1調(diào)用到3步驟之前,user2開始運(yùn)行到了1步驟了,由于單態(tài)的資源共享,則私有變量user被修改為user2
這種情況下,user1的步驟3用到的user.getId()實(shí)際用到是user2的對(duì)象。(我的理解是配置成singleton會(huì)造成資源混亂問題-對(duì)于有狀態(tài)的bean)
而如果是prototype的話,就不會(huì)出現(xiàn)資源共享的問題。(即不會(huì)出現(xiàn)線程安全的問題)
對(duì)于SSH來說,Bean的配置是沒錯(cuò)的,配置為singleton ;
實(shí)際應(yīng)該是這個(gè)例子不應(yīng)該用私有變量,這樣就使得這個(gè)Bean由無狀態(tài)變成了有狀態(tài)Bean.
還是應(yīng)該盡量使用無狀態(tài)Bean.如果在程序中出現(xiàn)私有變量(該bean會(huì)變?yōu)橛袪顟B(tài)的,一旦在其他線程中發(fā)生改變,就會(huì)產(chǎn)生線程不安全),解決方案就是盡量替換為方法中的參數(shù)。
對(duì)于每個(gè)訪問私有變量的方法增加變量傳入(參數(shù)傳入)或者通過ThreadLocal來獲取也是不錯(cuò)的方法。(重點(diǎn)解決方案,可以防止多線程帶來的不安全問題)
真正出現(xiàn)上面代碼問題的也是少數(shù),出現(xiàn)的時(shí)候,一般是為了圖方便,使用了一個(gè)很多方法都要用到的變量(變量在傳遞過程中發(fā)生改變,就會(huì)產(chǎn)生多線程不安全問題)。如果變量都需要用參數(shù)的方式傳遞多麻煩呀,這樣私有變量多好,不用參數(shù)那樣丑陋。但是丑陋并不代表不好,以對(duì)的,自己習(xí)慣的方式編程,才能盡量避免問題的發(fā)生。
引申:
Spring中的有狀態(tài)(Stateful)和無狀態(tài)(Stateless) (web中的并發(fā)通過單例可以避免)
- 1.通過上面的分析,相信大家已經(jīng)對(duì)有狀態(tài)和無狀態(tài)有了一定的理解。無狀態(tài)的Bean適合用不變模式,技術(shù)就是單例模式,這樣可以共享實(shí)例,提高性能。有狀態(tài)的Bean,多線程環(huán)境下不安全,那么適合用Prototype原型模式(解決多線程問題)。Prototype: 每次對(duì)bean的請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。
- 2.默認(rèn)情況下,從
Spring bean
工廠所取得的實(shí)例為singleton
(scope屬性為singleton),容器只存在一個(gè)共享的bean實(shí)例。 - 3.理解了兩者的關(guān)系,那么scope選擇的原則就很容易了:有狀態(tài)的bean都使用prototype作用域,而對(duì)無狀態(tài)的bean則應(yīng)該使用singleton作用域。
- 4.如Service層、Dao層用默認(rèn)singleton就行,雖然Service類也有dao這樣的屬性,但dao這些類都是沒有狀態(tài)信息的,也就是相當(dāng)于不變(immutable)類,所以不影響。Struts2中的Action因?yàn)闀?huì)有User、BizEntity這樣的實(shí)例對(duì)象,是有狀態(tài)信息的,在多線程環(huán)境下是不安全的,所以Struts2默認(rèn)的實(shí)現(xiàn)是Prototype模式。在
Spring
中,Struts2的Action中,scope要配成prototype
作用域。
Servlet、Struts中的有狀態(tài)和無狀態(tài):
1.Servlet體系結(jié)構(gòu)是建立在Java多線程機(jī)制之上的,它的生命周期是由Web 容器負(fù)責(zé)的。一個(gè)Servlet類在Application中只有一個(gè)實(shí)例存在,也就是有多個(gè)線程在使用這個(gè)實(shí)例。這是單例模式的應(yīng)用。無狀態(tài)的單例是線程安全的,但我們?nèi)绻赟ervlet里用了實(shí)例變量(私有變量),那么就變成有狀態(tài)了,是非線程安全的。如下面的用法就是不安全的,因?yàn)閡ser,out都是有狀態(tài)信息的。
/**? ?* 非線程安全的Servlet。? ?* @author Peter Wei? ?*? ?*/ ? public class UnSafeServlet HttpServlet{ ? ? ? ?? ? ? User user; ? ? ? PrintWriter out; ? ? ? ?? ? ? public void doGet (HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{ ? ? ? ? ? //do something... ? ? ? } ? } ?
Out,Request,Response,Session,Config,Page,PageContext是線程安全的,Application在整個(gè)系統(tǒng)內(nèi)被使用,所以不是線程安全的.
2.Struts1也是基于單例模式實(shí)現(xiàn),也就是只有一個(gè)Action實(shí)例供多線程使用。默認(rèn)的模式是前臺(tái)頁(yè)面數(shù)據(jù)通過actionForm傳入,在action中的excute方法接收,這樣action是無狀態(tài)的,所以一般情況下Strunts1是線程安全的。如果Action中用了實(shí)例變量,那么就變成有狀態(tài)了,同樣是非線程安全的。像下面這樣就是線程不安全的。
/**? ?* 非線程安全的Struts1示例? ?* ? ?* @author Peter Wei? ?* ? ?*/ ? public class UnSafeAction1 extends Action { ? ?? ? ? // 因?yàn)镾truts1是單例實(shí)現(xiàn),有狀態(tài)情況下,對(duì)象引用是非線程安全的 ? ? ? User user; ? ?? ? ? public void execute() { ? ? ? ? ? // do something... ? ? ? } ? ?? ? ? public User getUser() { ? ? ? ? ? return user; ? ? ? } ? ?? ? ? public void setUser(User user) { ? ? ? ? ? this.user = user; ? ? ? } ? } ? ?
3.Struts2默認(rèn)的實(shí)現(xiàn)是Prototype模式。也就是每個(gè)請(qǐng)求都新生成一個(gè)Action實(shí)例,所以不存在線程安全問題。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。
4.如何解決Servlet和Struts1的線程安全問題,當(dāng)我們能比較好的理解有狀態(tài)和無狀態(tài)的原理,自然很容易得出結(jié)論: 不要使用有狀態(tài)的bean,也就是不要用實(shí)例變量(私有變量) 。如果用有狀態(tài)的bean,就要用prototype模式,每次在注入的時(shí)候就重新創(chuàng)建一個(gè)bean,在多線程中互不影響。Struts1 user guide里有: Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.
總結(jié):
Stateless無狀態(tài)用單例Singleton
模式,Stateful有狀態(tài)就用原型Prototype模式。
Stateful 有狀態(tài)是多線程編碼的天敵,所以在開發(fā)中盡量用Stateless無狀態(tài),無狀態(tài)是不變(immutable)模式的應(yīng)用,有很多優(yōu)點(diǎn):不用管線程和同步的問題 ,如果值是不可變的,程序不用擔(dān)心多個(gè)線程改變共享狀態(tài),所以可以避免線程競(jìng)爭(zhēng)的bugs. 因?yàn)闆]有競(jìng)爭(zhēng),就不用用locks等機(jī)制,所以無狀態(tài)的不變機(jī)制,也可以避免產(chǎn)生死鎖現(xiàn)象。
到此這篇關(guān)于Spring Bean Scope 有狀態(tài)的Bean與無狀態(tài)的Bean的文章就介紹到這了,更多相關(guān) 有狀態(tài)的Bean與無狀態(tài)的Bean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Kotlin與java8的SAM轉(zhuǎn)換對(duì)比(進(jìn)階)
這篇文章主要介紹了Kotlin與java8的SAM轉(zhuǎn)換對(duì)比,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05java String 轉(zhuǎn)成Double二維數(shù)組的方法
下面小編就為大家?guī)硪黄猨ava String 轉(zhuǎn)成Double二維數(shù)組的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10解決使用ProcessBuilder踩到的坑及注意事項(xiàng)
這篇文章主要介紹了解決使用ProcessBuilder踩到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06MyBatis-Plus多數(shù)據(jù)源的示例代碼
本文主要介紹了MyBatis-Plus多數(shù)據(jù)源的示例代碼,包括依賴配置、數(shù)據(jù)源配置、Mapper 和 Service 的定義,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05