Spring Bean Scope 有狀態(tài)的Bean與無(wú)狀態(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。
無(wú)狀態(tài)會(huì)話bean:bean一旦實(shí)例化就被加進(jìn)會(huì)話池中,各個(gè)用戶都可以共用。即使用戶已經(jīng)消亡,bean 的生命期也不一定結(jié)束,它可能依然存在于會(huì)話池中,供其他用戶調(diào)用。由于沒(méi)有特定的用戶,那么也就不能保持某一用戶的狀態(tài),所以叫無(wú)狀態(tài)bean。但無(wú)狀態(tài)會(huì)話bean 并非沒(méi)有狀態(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è)用戶訪問(wèn),都調(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開(kāi)始運(yùn)行到了1步驟了,由于單態(tài)的資源共享,則私有變量user被修改為user2
這種情況下,user1的步驟3用到的user.getId()實(shí)際用到是user2的對(duì)象。(我的理解是配置成singleton會(huì)造成資源混亂問(wèn)題-對(duì)于有狀態(tài)的bean)
而如果是prototype的話,就不會(huì)出現(xiàn)資源共享的問(wèn)題。(即不會(huì)出現(xiàn)線程安全的問(wèn)題)
對(duì)于SSH來(lái)說(shuō),Bean的配置是沒(méi)錯(cuò)的,配置為singleton ;
實(shí)際應(yīng)該是這個(gè)例子不應(yīng)該用私有變量,這樣就使得這個(gè)Bean由無(wú)狀態(tài)變成了有狀態(tài)Bean.
還是應(yīng)該盡量使用無(wú)狀態(tài)Bean.如果在程序中出現(xiàn)私有變量(該bean會(huì)變?yōu)橛袪顟B(tài)的,一旦在其他線程中發(fā)生改變,就會(huì)產(chǎn)生線程不安全),解決方案就是盡量替換為方法中的參數(shù)。
對(duì)于每個(gè)訪問(wèn)私有變量的方法增加變量傳入(參數(shù)傳入)或者通過(guò)ThreadLocal來(lái)獲取也是不錯(cuò)的方法。(重點(diǎn)解決方案,可以防止多線程帶來(lái)的不安全問(wèn)題)
真正出現(xiàn)上面代碼問(wèn)題的也是少數(shù),出現(xiàn)的時(shí)候,一般是為了圖方便,使用了一個(gè)很多方法都要用到的變量(變量在傳遞過(guò)程中發(fā)生改變,就會(huì)產(chǎn)生多線程不安全問(wèn)題)。如果變量都需要用參數(shù)的方式傳遞多麻煩呀,這樣私有變量多好,不用參數(shù)那樣丑陋。但是丑陋并不代表不好,以對(duì)的,自己習(xí)慣的方式編程,才能盡量避免問(wèn)題的發(fā)生。
引申:
Spring中的有狀態(tài)(Stateful)和無(wú)狀態(tài)(Stateless) (web中的并發(fā)通過(guò)單例可以避免)
- 1.通過(guò)上面的分析,相信大家已經(jīng)對(duì)有狀態(tài)和無(wú)狀態(tài)有了一定的理解。無(wú)狀態(tài)的Bean適合用不變模式,技術(shù)就是單例模式,這樣可以共享實(shí)例,提高性能。有狀態(tài)的Bean,多線程環(huán)境下不安全,那么適合用Prototype原型模式(解決多線程問(wèn)題)。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ì)無(wú)狀態(tài)的bean則應(yīng)該使用singleton作用域。
- 4.如Service層、Dao層用默認(rèn)singleton就行,雖然Service類也有dao這樣的屬性,但dao這些類都是沒(méi)有狀態(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)和無(wú)狀態(tài):
1.Servlet體系結(jié)構(gòu)是建立在Java多線程機(jī)制之上的,它的生命周期是由Web 容器負(fù)責(zé)的。一個(gè)Servlet類在Application中只有一個(gè)實(shí)例存在,也就是有多個(gè)線程在使用這個(gè)實(shí)例。這是單例模式的應(yīng)用。無(wú)狀態(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ù)通過(guò)actionForm傳入,在action中的excute方法接收,這樣action是無(wú)狀態(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í)例,所以不存在線程安全問(wèn)題。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。
4.如何解決Servlet和Struts1的線程安全問(wèn)題,當(dāng)我們能比較好的理解有狀態(tài)和無(wú)狀態(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無(wú)狀態(tài)用單例Singleton模式,Stateful有狀態(tài)就用原型Prototype模式。
Stateful 有狀態(tài)是多線程編碼的天敵,所以在開(kāi)發(fā)中盡量用Stateless無(wú)狀態(tài),無(wú)狀態(tài)是不變(immutable)模式的應(yīng)用,有很多優(yōu)點(diǎn):不用管線程和同步的問(wèn)題 ,如果值是不可變的,程序不用擔(dān)心多個(gè)線程改變共享狀態(tài),所以可以避免線程競(jìng)爭(zhēng)的bugs. 因?yàn)闆](méi)有競(jìng)爭(zhēng),就不用用locks等機(jī)制,所以無(wú)狀態(tài)的不變機(jī)制,也可以避免產(chǎn)生死鎖現(xiàn)象。
到此這篇關(guān)于Spring Bean Scope 有狀態(tài)的Bean與無(wú)狀態(tài)的Bean的文章就介紹到這了,更多相關(guān) 有狀態(tài)的Bean與無(wú)狀態(tài)的Bean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javafx利用fxml變換場(chǎng)景的實(shí)現(xiàn)示例
本文主要介紹了Javafx利用fxml變換場(chǎng)景的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
mybatis中oracle實(shí)現(xiàn)分頁(yè)效果實(shí)例代碼
實(shí)現(xiàn)分頁(yè)的方式有很多,但常用的是通過(guò)SQL來(lái)顯示分頁(yè)。這篇文章主要介紹了mybatis中oracle實(shí)現(xiàn)分頁(yè)效果實(shí)例代碼,有興趣的可以了解一下。2017-04-04
IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法)
這篇文章主要介紹了IDEA最新激活碼2021(IDEA2020.3.2最新永久激活方法),本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
SpringCloud消息總線Bus配置中心實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了SpringCloud消息總線Bus配置中心實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
關(guān)于Maven parent.relativePath說(shuō)明
Maven中的relativePath用于指定父項(xiàng)目pom.xml的相對(duì)路徑,默認(rèn)值為../pom.xml,這個(gè)配置幫助Maven在構(gòu)建時(shí)定位父模塊的位置,確保模塊間的依賴關(guān)系正確,relativePath可以指向本地或遠(yuǎn)程倉(cāng)庫(kù)中的父項(xiàng)目,如果不需要尋找父項(xiàng)目,可以將其設(shè)置為空2024-09-09
Spring @Component自定義注解實(shí)現(xiàn)詳解
@Component是一個(gè)元注解,意思是可以注解其他類注解,如@Controller @Service @Repository @Aspect。官方的原話是:帶此注解的類看為組件,當(dāng)使用基于注解的配置和類路徑掃描的時(shí)候,這些類就會(huì)被實(shí)例化2022-09-09
基于SpringBoot實(shí)現(xiàn)Web應(yīng)用的登錄與退出功能
登錄與退出功能作為 Web 應(yīng)用中的基礎(chǔ)且重要的組成部分,直接關(guān)系到用戶的安全和隱私保護(hù),所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)Web應(yīng)用的登錄與退出功能,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-04-04
Spring+quartz實(shí)現(xiàn)定時(shí)發(fā)送郵件功能實(shí)例
spring提供的定時(shí)發(fā)送郵件功能一直深受廣大web開(kāi)發(fā)者的喜愛(ài),這篇文章主要介紹了Spring+quartz實(shí)現(xiàn)定時(shí)發(fā)送郵件功能實(shí)例,有興趣的可以了解一下。2017-03-03
Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

