說說 Spring Bean 的作用域

配置文件中定義 Bean 時,我們不但可以配置 Bean 的屬性值以及相互之間的依賴關系,還可以定義 Bean 的作用域 。作用域會對 Bean 的生命周期和創建方式產生影響 。

Bean 的作用域類型:

類型 說明
singleton 在 Spring 容器中僅存在一個 Bean 實例, Bean 以單例的形式存在。
prototype 每次從容器中調用 Bean 時,都會返回一個新的實例,即相當于執行 new XxxBean() 的實例化操作。
request 每次 http 請求都會創建一個新的 Bean , 僅用于 WebApplicationContext 環境。
session 同一個 http Session 共享一個 Bean ,不同的 http Session 使用不同的 Bean,僅用于 WebApplicationContext 環境。
globalSession 同一個全局 Session 共享一個 bean, 用于 Porlet, 僅用于 WebApplication 環境。

低版本的 Spring 中,僅支持兩個 Bean 作用域(singleton 與 prototype),所以之前的配置為 singleton=true/false。Spring 為了向后兼容,依然支持這種配置方式。我們推薦采用新的配置方式 scope=<作用域類型>

1 singleton 作用域

Spring 以容器的方式,使得我們僅需配置,即可得到天然的單例模式。

一般情況下,無狀態或者狀態不可變的類適合使用單例模式來實現, 不過 Spring 利用 AOP 和 LocalThread 的能力,對非線程安全的變量(狀態)進行了特殊處理,使的一些非線程安全的類(持有 Connection 的 DAO 類)變成了線程安全的類 。

因為 Spring 的超強能力,所以在實際應用中,大部分 Bean 都能以單例方式運行 ,這也是 bean 的默認作用域指定為 singleton 的原因 。

singleton 的 Bean 在同一個 Spring IoC 容器中只會一個實例。

配置:

<!-- singleton 作用域 -->
<bean id="author" class="net.deniro.spring4.bean.Author" scope="singleton"/>
<bean id="book1" class="net.deniro.spring4.bean.Book" p:author-ref="author"/>
<bean id="book2" class="net.deniro.spring4.bean.Book" p:author-ref="author"/>
<bean id="book3" class="net.deniro.spring4.bean.Book" p:author-ref="author"/>

單元測試:

System.out.println(((Book) context.getBean("book1")).getAuthor().hashCode());
System.out.println(((Book) context.getBean("book2")).getAuthor().hashCode());
System.out.println(((Book) context.getBean("book3")).getAuthor().hashCode());

輸出結果:

813656972
813656972
813656972

這證明 Author 類在容器中是一個單例。

singleton 作用域

不僅在配置文件中注入的 author 引用的是同一個 author Bean,任何通過容器的 getBean("author") 返回的實例,引用的也是同一個 Bean。

默認情況下, Spring 的 ApplicationContext 容器在啟動時,會自動實例化所有 singleton 的 Bean 并緩存在容器中 。 雖然啟動時會多花費一些時間,但是有這些好處:

  • 對 Bean 提前進行實例化操作會及早發現一些潛在的配置問題。
  • Bean 以緩存的方式保存,當運行時調用該 Bean 時就無須再次實例化咯,因此提高運行效率 。

如果不希望在容器啟動時提前實例化 singleton 的 Bean ,那么可以通過 lazy-init 屬性進行控制:

<bean id="book1" class="net.deniro.spring4.bean.Book" p:author-ref="author" lazy-init="true"/>

如果某個 Bean 被其他需要提前實例化的 Bean 所引用,那么它即使配置了 lazy-init="true" ,也仍然會被提前實例化。

2 prototype 作用域

配置了 scope="prototype" 的 bean 為非單例作用域。

<bean id="author10" class="net.deniro.spring4.bean.Author" scope="prototype"/>
<bean id="book11" class="net.deniro.spring4.bean.Book" p:author-ref="author10"/>
<bean id="book12" class="net.deniro.spring4.bean.Book" p:author-ref="author10"/>
<bean id="book13" class="net.deniro.spring4.bean.Book" p:author-ref="author10"/>

單元測試:

System.out.println(((Book) context.getBean("book11")).getAuthor().hashCode());
System.out.println(((Book) context.getBean("book12")).getAuthor().hashCode());
System.out.println(((Book) context.getBean("book13")).getAuthor().hashCode());

輸出結果:

2048425748
1863932867s
1373810119

這證明 Author 類在容器中是不同的實例。

prototype 作用域

在默認情況下, Spring 容器在啟動時不實例化 prototype 的 bean ,此外, Spring 容器將 prototype 的 bean 交給調用者后,就不再負責管理它的生命周期咯。

3 與 Web 應用環境相關的作用域

如果使用 Spring 的 WebApplicationContext ,則可以使用另外 3 種 Bean 的作用域 (request、session 和 gloableSession)。

3.1 配置監聽器

首先必須在 Web 容器中進行一些配置:

高版本的 Web 容器( Servlet 2.3 +)中,配置 http 請求監聽器:

<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

低版本的 Web 容器,需要配置 http 請求過濾器(RequestContextFilter)。

它與 ContextLoaderListener 的區別是:

  • ContextLoaderListener 實現了 ServletContextListener 監聽器接口, 它只負責監聽 web 容器的啟動和關閉事件 。
  • RequestContextListener 實現了 ServletRequestListener 監聽器接口,它監聽 HTTP 請求事件, Web 服務器的每一次請求都會通知它 。

3.2 request 作用域

request 作用域的 Bean 對應一個 HTTP 請求和生命周期 。

假設:

<bean id="author" class="net.deniro.spring4.bean.Author" scope="request"/>

每次 HTTP 請求調用 author Bean 時, Spring 容器就會創建一個新的 author Bean ;請求處理完畢,就會銷毀這個 Bean。

3.3 session 作用域

假設:

<bean id="author" class="net.deniro.spring4.bean.Author" scope="session"/>

author Bean 的作用于橫跨整個 HTTP Session。Session 中的所有 HTTP 請求會共享同一個 author Bean. 只有當 HTTP Session 結束后,author 實例才會被銷毀 。

3.4 globalSession 作用域

假設:

<bean id="author" class="net.deniro.spring4.bean.Author" scope="globalSession"/>

globalSession 的作用域類似于 session 作用域, 不過僅在 Portlet 的 Web 應用中使用 。 Portlet 定義了全局 Session,它被組成 Portlet Web 應用的所有子 Portlet 共享。如果不在 Portlet 的 Web 應用下,globalSession 等價于 session。

4 作用域依賴

假設我們需要在 非 web 作用域下的 Bean 中引用 web 作用域下的 Bean,那么這里我們就需要使用 Spring AOP 的功能。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       ">

    <!-- 非 web 作用域引用 web 作用域-->
    <bean id="author20" class="net.deniro.spring4.bean.Author" scope="request">
        <!-- 創建代理-->
        <aop:scoped-proxy/>
    </bean>
    <bean id="book20" class="net.deniro.spring4.bean.Book" p:author-ref="author20"/>

</beans>

首先引入了 AOP 文件頭說明。

author Bean 的作用域是 request , 它被 singleton 作用域的 book Bean 所引用 。 為了使 book 能從 request 的作用域中獲取 author 的引用, 這里使用了 Spring AOP 為 book Bean 聲明了一個代理類。

當 book Bean 在 web 環境中調用 author Bean 時, Spring AOP 將啟動動態代理機制智能判斷 author Bean 處于哪一個 HTTP 請求線程中,并從對應的 HTTP 請求線程中回去對應的 author Bean。

request 作用域下的請求線程

Spring 通過動態代理技術,能夠讓單例的 book bean 引用到對應 HTTP 請求的 author Bean。

動態代理會判斷當前的 book 位于哪一個線程中,然后根據這個線程找到對應的 HttpRequest,接著再從 HttpRequest 中獲取對應的 author。根據容器的特性,一個 HTTP 請求對應一個獨立的線程。

因為 Java 語言只能對接口實現自動代理,因此,Spring 是通過 CGLib 庫來實現類的動態代理的,所以如果有需要,請在類路徑中加入 CGLib 庫。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容