Spring 中JavaBean的作用域

Spring Framework支持五種作用域(其中有三種只能用在基于web的SpringApplicationContext)。

singleton

在每個Spring IoC容器中一個bean定義對應一個對象實例倦青。

prototype

一個bean定義對應多個對象實例瓮床。

request

在一次HTTP請求中,一個bean定義對應一個實例姨夹;即每次HTTP請求將會有各自的bean實例纤垂,它們依據某個bean定義創(chuàng)建而成矾策。該作用域僅在基于web的SpringApplicationContext情形下有效磷账。

session

在一個HTTPSession中,一個bean定義對應一個實例贾虽。該作用域僅在基于web的SpringApplicationContext情形下有效逃糟。

global session

在一個全局的HTTPSession中,一個bean定義對應一個實例蓬豁。典型情況下绰咽,僅在使用portlet context的時候有效。該作用域僅在基于web的SpringApplicationContext情形下有效地粪。

[@more@]

1.Singleton作用域

當一個bean的作用域為singleton,那么Spring IoC容器中只會存在一個共享的bean實例取募,并且所有對bean的請求,只要id與該bean定義相匹配蟆技,則只會返回bean的同一實例玩敏。

換言之,當把一個bean定義設置為singlton作用域時质礼,Spring IoC容器只會創(chuàng)建該bean定義的唯一實例旺聚。這個單一實例會被存儲到單例緩存(singleton cache)中,并且所有針對該bean的后續(xù)請求和引用都將返回被緩存的對象實例眶蕉。

下圖演示了Spring的singleton作用域砰粹。

請注意Spring的singleton bean概念與“四人幫”(GoF)模式一書中定義的Singleton模式是完全不同的。經典的GoF Singleton模式中所謂的對象范圍是指在每一個ClassLoader中指定class創(chuàng)建的實例有且僅有一個造挽。把Spring的singleton作用域描述成一個container對應一個bean實例最為貼切碱璃。亦即弄痹,假如在單個Spring容器內定義了某個指定class的bean,那么Spring容器將會創(chuàng)建一個且僅有一個由該bean定義指定的類實例嵌器。

Singleton作用域是Spring中的缺省作用域界酒。

2.Prototype

Prototype作用域的bean會導致在每次對該bean請求(將其注入到另一個bean中,或者以程序的方式調用容器的getBean()方法)時都會創(chuàng)建一個新的bean實例嘴秸。根據經驗毁欣,對所有有狀態(tài)的bean應該使用prototype作用域,而對無狀態(tài)的bean則應該使用singleton作用域岳掐。

下圖演示了Spring的prototype作用域凭疮。請注意,典型情況下串述,DAO不會被配置成prototype执解,因為一個典型的DAO不會持有任何會話狀態(tài),因此應該使用singleton作用域纲酗。

對于prototype作用域的bean衰腌,有一點非常重要,那就是Spring不能對一個prototype bean的整個生命周期負責:容器在初始化觅赊、配置右蕊、裝飾或者是裝配完一個prototype實例后,將它交給客戶端吮螺,隨后就對該prototype實例不聞不問了饶囚。不管何種作用域,容器都會調用所有對象的初始化生命周期回調方法鸠补,而對prototype而言萝风,任何配置好的析構生命周期回調方法都將不會被調用。清除prototype作用域的對象并釋放任何prototype bean所持有的昂貴資源紫岩,都是客戶端代碼的職責规惰。(讓Spring容器釋放被singleton作用域bean占用資源的一種可行方式是,通過使用bean的后置處理器泉蝌,該處理器持有要被清除的bean的引用歇万。)

談及prototype作用域的bean時,在某些方面你可以將Spring容器的角色看作是Java new操作符的替代者梨与。任何遲于該時間點的生命周期事宜都得交由客戶端來處理堕花。在Section 3.5.1,“Lifecycle接口”一節(jié)中會進一步講述Spring IoC容器中的bean生命周期。

向后兼容性:在XML中指定生命周期作用域

如果你在bean定義文件中引用'spring-beans.dtd' DTD粥鞋,要顯式說明bean的生命周期作用域你必須使用"singleton"屬性(記住singleton生命周期作用域是默認的)缘挽。 如果引用的是'spring-beans-2.0.dtd' DTD或者是Spring 2.0 XSD schema,那么需要使用"scope"屬性(因為"singleton"屬性被刪除了,新的DTD和XSD文件使用"scope"屬性)壕曼。

簡單地說苏研,如果你用"singleton"屬性那么就必須在那個文件里引用'spring-beans.dtd' DTD。 如果你用"scope"屬性那么必須 在那個文件里引用'spring-beans-2.0.dtd' DTD或'spring-beans-2.0.xsd' XSD腮郊。

3.其他作用域

其他作用域摹蘑,即request、session以及global session僅在基于web的應用中使用(不必關心你所采用的是什么web應用框架)轧飞。

Note

下面介紹的作用域僅僅在使用基于web的Spring ApplicationContext實現(如XmlWebApplicationContext)時有用衅鹿。如果在普通的Spring IoC容器中,比如像XmlBeanFactory或ClassPathXmlApplicationContext过咬,嘗試使用這些作用域大渤,你將會得到一個IllegalStateException異常(未知的bean作用域)。

3.1.初始化web配置

要使用request掸绞、session和global session作用域的bean(即具有web作用域的bean)泵三,在開始設置bean定義之前,還要做少量的初始配置衔掸。請注意烫幕,假如你只想要“常規(guī)的”作用域,也就是singleton和prototype敞映,就不需要這一額外的設置较曼。

在目前的情況下,根據你的特定servlet環(huán)境驱显,有多種方法來完成這一初始設置诗芜。如果你使用的是Servlet 2.4及以上的web容器,那么你僅需要在web應用的XML聲明文件web.xml中增加下述ContextListener即可

org.springframework.web.context.request.RequestContextListener

如果你用的是早期版本的web容器(Servlet 2.4以前)埃疫,那么你要使用一個javax.servlet.Filter的實現。請看下面的web.xml配置片段:

requestContextFilter

org.springframework.web.filter.RequestContextFilter

requestContextFilter

/*

3.2. Request作用域

考慮下面bean定義:

針對每次HTTP請求孩哑,Spring容器會根據loginAction bean定義創(chuàng)建一個全新的LoginAction bean實例栓霜,且該loginAction bean實例僅在當前HTTP request內有效,因此可以根據需要放心的更改所建實例的內部狀態(tài)横蜒,而其他請求中根據loginAction bean定義創(chuàng)建的實例胳蛮,將不會看到這些特定于某個請求的狀態(tài)變化。當處理請求結束丛晌,request作用域的bean實例將被銷毀仅炊。

3.3. Session作用域

考慮下面bean定義:

針對某個HTTP Session,Spring容器會根據userPreferences bean定義創(chuàng)建一個全新的userPreferences bean實例澎蛛,且該userPreferences bean僅在當前HTTP Session內有效抚垄。與request作用域一樣,你可以根據需要放心的更改所創(chuàng)建實例的內部狀態(tài),而別的HTTP Session中根據userPreferences創(chuàng)建的實例呆馁,將不會看到這些特定于某個HTTP Session的狀態(tài)變化桐经。當HTTP Session最終被廢棄的時候,在該HTTP Session作用域內的bean也會被廢棄掉浙滤。

3.4. global session作用域

考慮下面bean定義:

global session作用域類似于標準的HTTP Session作用域阴挣,不過它僅僅在基于portlet的web應用中才有意義。Portlet規(guī)范定義了全局Session的概念纺腊,它被所有構成某個portlet web應用的各種不同的portlet所共享畔咧。在global session作用域中定義的bean被限定于全局portlet Session的生命周期范圍內。

請注意揖膜,假如你在編寫一個標準的基于Servlet的web應用盒卸,并且定義了一個或多個具有global session作用域的bean,系統(tǒng)會使用標準的HTTP Session作用域次氨,并且不會引起任何錯誤蔽介。

3.5.作用域bean與依賴

能夠在HTTP request或者Session(甚至自定義)作用域中定義bean固然很好,但是Spring IoC容器除了管理對象(bean)的實例化煮寡,同時還負責協作者(或者叫依賴)的實例化虹蓄。如果你打算將一個Http request范圍的bean注入到另一個bean中,那么需要注入一個AOP代理來替代被注入的作用域bean幸撕。也就是說薇组,你需要注入一個代理對象,該對象具有與被代理對象一樣的公共接口坐儿,而容器則可以足夠智能的從相關作用域中(比如一個HTTP request)獲取到真實的目標對象律胀,并把方法調用委派給實際的對象。

不能和作用域為singleton或prototype的bean一起使用貌矿。為singleton bean創(chuàng)建一個scoped proxy將拋出BeanCreationException異常炭菌。

讓我們看一下將相關作用域bean作為依賴的配置,配置并不復雜(只有一行)逛漫,但是理解“為何這么做”以及“如何做”是很重要的黑低。

在XML配置文件中,要創(chuàng)建一個作用域bean的代理酌毡,只需要在作用域bean定義里插入一個子元素即可(你可能還需要在classpath里包含CGLIB庫克握,這樣容器就能夠實現基于class的代理;還可能要使用基于XSD的配置)枷踏。上述XML配置展示了“如何做”菩暗,現在討論“為何這么做”。在作用域為request旭蠕、session以及globalSession的bean定義里停团,為什么需要這個元素呢旷坦?下面我們從去掉元素的XML配置開始說起:

從上述配置中可以很明顯的看到singleton bean userManager被注入了一個指向HTTP Session作用域bean userPreferences的引用。singleton userManager bean會被容器僅實例化一次客蹋,并且其依賴(userPreferences bean)也僅被注入一次塞蹭。這意味著,userManager在理論上只會操作同一個userPreferences對象讶坯,即原先被注入的那個bean番电。而注入一個HTTP Session作用域的bean作為依賴,有違我們的初衷辆琅。因為我們想要的只是一個userManager對象漱办,在它進入一個HTTP Session生命周期時,我們希望去使用一個HTTP Session的userPreferences對象婉烟。

當注入某種類型對象時娩井,該對象實現了和UserPreferences類一樣的公共接口(即UserPreferences實例)。并且不論我們底層選擇了何種作用域機制(HTTP request似袁、Session等等)洞辣,容器都會足夠智能的獲取到真正的UserPreferences對象,因此我們需要將該對象的代理注入到userManager bean中,而userManager bean并不會意識到它所持有的是一個指向UserPreferences引用的代理昙衅。在本例中扬霜,當UserManager實例調用了一個使用UserPreferences對象的方法時,實際調用的是代理對象的方法而涉。隨后代理對象會從HTTP Session獲取真正的UserPreferences對象著瓶,并將方法調用委派給獲取到的實際的UserPreferences對象。

這就是為什么當你將request啼县、session以及globalSession作用域bean注入到協作對象中時需要如下正確而完整的配置:Java代碼

4自定義作用域

在Spring 2.0中材原,Spring的bean作用域機制是可以擴展的。這意味著季眷,你不僅可以使用Spring提供的預定義bean作用域余蟹; 還可以定義自己的作用域,甚至重新定義現有的作用域(不提倡這么做瘟裸,而且你不能覆蓋內置的singleton和prototype作用域)客叉。

作用域由接口org.springframework.beans.factory.config.Scope定義。要將你自己的自定義作用域集成到Spring容器中话告,需要實現該接口。它本身非常簡單卵慰,只有兩個方法沙郭,分別用于底層存儲機制獲取和刪除對象。自定義作用域可能超出了本參考手冊的討論范圍裳朋,但你可以參考一下Spring提供的Scope實現病线,以便于去如何著手編寫自己的Scope實現。

在實現一個或多個自定義Scope并測試通過之后,接下來就是如何讓Spring容器識別你的新作用域送挑。ConfigurableBeanFactory接口聲明了給Spring容器注冊新Scope的主要方法绑莺。(大部分隨Spring一起發(fā)布的BeanFactory具體實現類都實現了該接口);該接口的主要方法如下所示:

void registerScope(String scopeName, Scope scope);

registerScope(..)方法的第一個參數是與作用域相關的全局唯一名稱惕耕;Spring容器中該名稱的范例有singleton和prototype纺裁。registerScope(..)方法的第二個參數是你打算注冊和使用的自定義Scope實現的一個實例。

假設你已經寫好了自己的自定義Scope實現司澎,并且已經將其進行了注冊:

// note: the ThreadScope class does not exist; I made it up for the sake of this example

Scope customScope = new ThreadScope();

beanFactory.registerScope("thread", scope);

然后你就可以像下面這樣創(chuàng)建與自定義Scope的作用域規(guī)則相吻合的bean定義了:

如果你有自己的自定義Scope實現欺缘,你不僅可以采用編程的方式注冊自定義作用域,還可以使用BeanFactoryPostProcessor實現:CustomScopeConfigurer類挤安,以聲明的方式注冊Scope谚殊。BeanFactoryPostProcessor接口是擴展Spring IoC容器的基本方法之一,在本章的BeanFactoryPostProcessor中將會介紹蛤铜。

使用CustomScopeConfigurer嫩絮,以聲明方式注冊自定義Scope的方法如下所示:

Java代碼既允許你指定實際的Class實例作為entry的值,也可以指定實際的Scope實現類實例围肥;詳情請參見CustomScopeConfigurer類的JavaDoc

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末剿干,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子虐先,更是在濱河造成了極大的恐慌怨愤,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛹批,死亡現場離奇詭異撰洗,居然都是意外死亡,警方通過查閱死者的電腦和手機腐芍,發(fā)現死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門差导,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猪勇,你說我怎么就攤上這事设褐。” “怎么了泣刹?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵助析,是天一觀的道長。 經常有香客問我椅您,道長外冀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任掀泳,我火速辦了婚禮雪隧,結果婚禮上西轩,老公的妹妹穿的比我還像新娘。我一直安慰自己脑沿,他們只是感情好藕畔,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著庄拇,像睡著了一般注服。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丛忆,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天祠汇,我揣著相機與錄音,去河邊找鬼熄诡。 笑死可很,一個胖子當著我的面吹牛,可吹牛的內容都是我干的凰浮。 我是一名探鬼主播我抠,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袜茧!你這毒婦竟也來了菜拓?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤笛厦,失蹤者是張志新(化名)和其女友劉穎纳鼎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體裳凸,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡贱鄙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了姨谷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逗宁。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖梦湘,靈堂內的尸體忽然破棺而出瞎颗,到底是詐尸還是另有隱情,我是刑警寧澤捌议,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布哼拔,位于F島的核電站,受9級特大地震影響瓣颅,放射性物質發(fā)生泄漏管挟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一弄捕、第九天 我趴在偏房一處隱蔽的房頂上張望僻孝。 院中可真熱鬧,春花似錦守谓、人聲如沸穿铆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荞雏。三九已至,卻和暖如春平酿,著一層夾襖步出監(jiān)牢的瞬間凤优,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工蜈彼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筑辨,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓幸逆,卻偏偏與公主長得像棍辕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子还绘,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容

  • 文章作者:Tyan博客:noahsnail.com 3.5 Bean scopes When you create...
    SnailTyan閱讀 1,879評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理楚昭,服務發(fā)現,斷路器拍顷,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 來源:關于Spring IOC (DI-依賴注入)你需要知道的一切作者:zejian Dao層(AccountDa...
    楊井閱讀 5,326評論 0 27
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架抚太。Spring框架的核心功能可以應用在任何Jav...
    jemmm閱讀 16,441評論 1 133
  • 暴走羅馬是一種游客必備的態(tài)度,但是享受他們精致而慵懶的慢生活是我的態(tài)度昔案。 這里有最熱情如火的意大利人尿贫,如同他們耀眼...
    孫大貓閱讀 454評論 4 5