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