1. 程式人生 > 程式設計 >深入瞭解Spring中Bean的作用域和生命週期

深入瞭解Spring中Bean的作用域和生命週期

作用域的種類

Spring 容器在初始化一個 Bean 的例項時,同時會指定該例項的作用域。Spring3 為 Bean 定義了五種作用域,具體如下。

1)singleton

單例模式,使用 singleton 定義的 Bean 在 Spring 容器中只有一個例項,這也是 Bean 預設的作用域。

2)prototype

原型模式,每次通過 Spring 容器獲取 prototype 定義的 Bean 時,容器都將建立一個新的 Bean 例項。

3)request

在一次 HTTP 請求中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Request 內有效。

4)session

在一次 HTTP Session 中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Session 內有效。

5)global Session

在一個全域性的 HTTP Session 中,容器會返回該 Bean 的同一個例項。該作用域僅在使用 portlet context 時有效。

在上述五種作用域中,singleton 和 prototype 是最常用的兩種,接下來將對這兩種作用域進行詳細講解。

singleton 作用域

singleton 是 Spring 容器預設的作用域,當一個 Bean 的作用域為 singleton 時,Spring 容器中只會存在一個共享的 Bean 例項,並且所有對 Bean 的請求,只要 id 與該 Bean 定義相匹配,就只會返回 Bean 的同一個例項。

通常情況下,這種單例模式對於無會話狀態的 Bean(如 DAO 層、Service 層)來說,是最理想的選擇。

在 Spring 配置檔案中,可以使用 <bean> 元素的 scope 屬性,將 Bean 的作用域定義成 singleton,其配置方式如下所示:

<bean id="person" class="com.mengma.scope.Person" scope="singleton"/>

在專案的 src 目錄下建立一個名為 com.mengma.scope 的包,在該包下建立 Person 類,類中不需要新增任何成員,然後建立 Spring 的配置檔案 applicationContext.xml,將上述 Bean 的定義方式寫入配置檔案中,最後建立一個名為 PersonTest 的測試類,編輯後如下所示。

package com.mengma.scope;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PersonTest {
@Test
public void test() {
// 定義Spring配置檔案路徑
String xmlPath = "com/mengma/scope/applicationContext.xml";
// 初始化Spring容器,載入配置檔案,並對bean進行例項化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
xmlPath);
// 輸出獲得例項
System.out.println(applicationContext.getBean("person"));
System.out.println(applicationContext.getBean("person"));
}
}

使用 JUnit 測試執行 test() 方法,執行成功後,控制檯的輸出結果如圖 1 所示。

圖 1 輸出結果

從圖 1 中可以看到,兩次輸出的結果相同,這說明 Spring 容器只建立了一個 Person 類的例項。由於 Spring 容器預設作用域是 singleton,如果不設定 scope="singleton",則其輸出結果也將是一個例項。

prototype 作用域

使用 prototype 作用域的 Bean 會在每次請求該 Bean 時都會建立一個新的 Bean 例項。因此對需要保持會話狀態的 Bean(如 Struts2 的 Action 類)應該使用 prototype 作用域。

在 Spring 配置檔案中,要將 Bean 定義為 prototype 作用域,只需將 <bean> 元素的 scope 屬性值定義成 prototype,其示例程式碼如下所示:

<bean id="person" class="com.mengma.scope.Person" scope="prototype"/>

將《singleton作用域》部分中的配置檔案更改成上述程式碼形式後,再次執行 test() 方法,控制檯的輸出結果如圖 2 所示。

圖 2 輸出結果

從圖 2 的輸出結果中可以看到,兩次輸出的結果並不相同,這說明在 prototype 作用域下,Spring 容器建立了兩個不同的 Person 例項。

生命週期

而對於 prototype 作用域的 Bean,Spring 只負責建立,當容器建立了 Bean 的例項後,Bean 的例項就交給客戶端程式碼管理,Spring 容器將不再跟蹤其生命週期。每次客戶端請求 prototype 作用域的 Bean 時,Spring 容器都會建立一個新的例項,並且不會管那些被配置成 prototype 作用域的 Bean 的生命週期。

瞭解 Spring 生命週期的意義就在於,可以利用 Bean 在其存活期間的指定時刻完成一些相關操作。這種時刻可能有很多,但一般情況下,會在 Bean 被初始化後和被銷燬前執行一些相關操作。

在 Spring 中,Bean 的生命週期是一個很複雜的執行過程,我們可以利用 Spring 提供的方法定製 Bean 的建立過程。

當一個 Bean 被載入到 Spring 容器時,它就具有了生命,而 Spring 容器在保證一個 Bean 能夠使用之前,會進行很多工作。Spring 容器中 Bean 的生命週期流程如圖3所示。

圖3Bean 的生命週期

Bean 生命週期的整個執行過程描述如下。

1)根據配置情況呼叫 Bean 構造方法或工廠方法例項化 Bean。

2)利用依賴注入完成 Bean 中所有屬性值的配置注入。

3)如果 Bean 實現了 BeanNameAware 介面,則 Spring 呼叫 Bean 的 setBeanName() 方法傳入當前 Bean 的 id 值。

4)如果 Bean 實現了 BeanFactoryAware 介面,則 Spring 呼叫 setBeanFactory() 方法傳入當前工廠例項的引用。

5)如果 Bean 實現了 ApplicationContextAware 介面,則 Spring 呼叫 setApplicationContext() 方法傳入當前 ApplicationContext 例項的引用。

6)如果 BeanPostProcessor 和 Bean 關聯,則 Spring 將呼叫該介面的預初始化方法 postProcessBeforeInitialzation() 對 Bean 進行加工操作,此處非常重要,Spring 的 AOP 就是利用它實現的。

7)如果 Bean 實現了 InitializingBean 介面,則 Spring 將呼叫 afterPropertiesSet() 方法。

8)如果在配置檔案中通過 init-method 屬性指定了初始化方法,則呼叫該初始化方法。

9)如果 BeanPostProcessor 和 Bean 關聯,則 Spring 將呼叫該介面的初始化方法 postProcessAfterInitialization()。此時,Bean 已經可以被應用系統使用了。

10)如果在 <bean> 中指定了該 Bean 的作用範圍為 scope="singleton",則將該 Bean 放入 Spring IoC 的快取池中,將觸發 Spring 對該 Bean 的生命週期管理;如果在 <bean> 中指定了該 Bean 的作用範圍為 scope="prototype",則將該 Bean 交給呼叫者,呼叫者管理該 Bean 的生命週期,Spring 不再管理該 Bean。

11)如果 Bean 實現了 DisposableBean 介面,則 Spring 會呼叫 destory() 方法將 Spring 中的 Bean 銷燬;如果在配置檔案中通過 destory-method 屬性指定了 Bean 的銷燬方法,則 Spring 將呼叫該方法對 Bean 進行銷燬。

Spring 為 Bean 提供了細緻全面的生命週期過程,通過實現特定的介面或 <bean> 的屬性設定,都可以對 Bean 的生命週期過程產生影響。雖然可以隨意配置 <bean> 的屬性,但是建議不要過多地使用 Bean 實現介面,因為這樣會導致程式碼和 Spring 的聚合過於緊密。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。