Spring詳解(十一)----Bean 的作用域
1、Bean 的作用域介紹
預設情況下,Spring 只為每個在 IOC 容器裡宣告的 Bean 建立唯一 一個例項,並且整個 IOC 容器範圍內都能共享該例項,所有後續的物件通過getBean() 呼叫和 Bean 的引用都將返回這個唯一的 Bean 例項。但是如果我們需要不同例項的Bean呢?這時就要提到Bean的作用域了,在Spring中預設的作用域為 Singleton,它是所有 Bean 的預設作用域。而在Spring 中提供了 5 種作用域,它會根據情況來決定是否生成新的物件,如下:
- singleton:單例模式,使用 singleton 定義的 Bean 在 Spring 容器中只有一個例項,這也是 Bean 預設的作用域。
- prototype:原型模式(多例模式),每次通過 Spring 容器獲取 prototype 定義的 Bean 時,容器都將建立一個新的 Bean 例項,即每次呼叫getBean()時。
- request:請求模式,請求用於Web開發,在一次 HTTP 請求中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Request 內有效。
- session:會話模式,用於Web開發,在一次 HTTP Session 中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Session 內有效。
global Session(Spring5已經去除了):全域性會話模式,在一個全域性的 HTTP Session 中,容器會返回該 Bean 的同一個例項。該作用域僅在使用 portlet context 時有效。一般用於 Porlet 應用環境 , 分散式系統存在全域性 session 概念(單點登入),如果不是 porlet 環境,globalSession 等同於 Session
=====================================
我們可以通過XML<bean> 元素中的 scope 屬性 或者 @Scope 註解 來設定,例如:
- XML 中設定作用域:<bean id="" class="" scope="prototype" />
- 註解設定作用域: @Scope("prototype") 或者 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
在上述五種作用域中,實際開發中 singleton 和 prototype 是最常用的兩種,接下來將對這兩種作用域進行詳細講解。
- singleton 作用域:singleton 是 Spring 容器預設的作用域,當一個 Bean 的作用域為 singleton 時,Spring 容器中只會存在一個共享的 Bean 例項,並且所有對 Bean 的請求,只要 id 與該 Bean 定義相匹配,就只會返回 Bean 的同一個例項。通常情況下,這種單例模式對於無會話狀態的 Bean(如 DAO 層、Service 層)來說,是最理想的選擇。
- prototype 作用域:使用 prototype 作用域的 Bean 會在每次請求該 Bean 時都會建立一個新的 Bean 例項。因此對需要保持會話狀態的 Bean應該使用 prototype 作用域。
2、XML配置舉例
①、首先建立一個User類,程式碼如下:
/** * 使用者實體類 */ public class User { private int userId; private String userName; private int userAge; private String userPwd; private String userAddress; //getter、setter省略...(這裡就不要toString方法了,因為要列印物件的地址) }
②、在 Spring 的配置檔案我們將 scope 屬性的值設定為 prototype 多例模式,xml如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--建立一個User例項,作用域設定為prototype 多例模式--> <bean id="user" class="com.thr.pojo.User" scope="prototype"/> </beans>
③、測試程式碼,這裡分別獲取了三個User物件:
/** * 測試程式碼 */ public class SpringTest { public static void main(String[] args) { //1.初始化Spring容器,載入配置檔案 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.通過容器獲取例項 User user1 = applicationContext.getBean("user", User.class); User user2 = applicationContext.getBean("user", User.class); User user3 = applicationContext.getBean("user", User.class); //3.呼叫例項中的屬性 System.out.println(user1); System.out.println(user2); System.out.println(user3); } }
④、執行測試程式碼,檢視控制檯列印的結果:
可以很明顯的發現,這三個User物件的地址是不一樣的,說明分別建立了三個不同的User物件。
3、註解配置舉例
①、首先建立一個User類,程式碼參考上面的。
②、建立PojoConfig類,用來啟動容器和註冊Bean物件:
/** * 啟動容器和註冊Bean物件 */ @ComponentScan(basePackages = "com.thr.pojo") @Configuration public class PojoConfig { public PojoConfig() { System.out.println("PojoConfig容器初始化成功..."); } //例項化User物件 @Bean(name = "user") //顯示設定單例模式 @Scope(value = "singleton") public User user(){ return new User(); } }
③、測試程式碼,這裡分別獲取了三個User物件,注意這裡需要使用AnnotationConfigApplicationContext類來獲取:
/** * 測試程式碼 */ public class SpringTest1 { public static void main(String[] args) { //1.初始化Spring容器,通過註解載入 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PojoConfig.class); //2.通過容器獲取例項 User user1 = applicationContext.getBean("user", User.class); User user2 = applicationContext.getBean("user", User.class); User user3 = applicationContext.getBean("user", User.class); //3.呼叫例項中的屬性 System.out.println(user1); System.out.println(user2); System.out.println(user3); } }
④、執行測試程式碼,檢視控制檯列印的結果:
由於註冊 Bean 時Scope設定為 singleton 單例模式,所以建立的多個 User 肯定是同一個,可以發現結果是一樣的。