詳解Spring中Bean的作用域與生命周期
摘要:在利用Spring進行IOC配置時,關於bean的配置和使用一直都是比較重要的一部分,同時如何合理的使用和創建bean對象,也是小夥伴們在學習和使用Spring時需要注意的部分,所以這一篇文章我就來和大家講一下有關Spring中bean的作用域和其生命周期。
本文分享自華為雲社區《詳解Spring中Bean的作用域與生命周期》,原文作者:灰小猿。
在利用Spring進行IOC配置時,關於bean的配置和使用一直都是比較重要的一部分,同時如何合理的使用和創建bean對象,也是小夥伴們在學習和使用Spring時需要注意的部分,所以這一篇文章我就來和大家講一下有關Spring中bean的作用域和其生命周期。
一、Bean的作用域
首先我們來講一下有關於bean的作用域,
一般情況下,我們書寫在IOC容器中的配置信息,會在我們的IOC容器運行時被創建,這就導致我們通過IOC容器獲取到bean對象的時候,往往都是獲取到了單實例的Bean對象,
這樣就意味着無論我們使用多少個getBean()方法,獲取到的同一個JavaBean都是同一個對象,這就是單實例Bean,整個項目都會共享這一個bean對象。
在Spring中,可以在<bean>元素的scope屬性里設置bean的作用域,以決定這個bean是單實例的還是多實例的。Scope屬性有四個參數,具體的使用可以看下圖:
1、單實例Bean聲明
默認情況下,Spring只為每個在IOC容器里聲明的bean創建唯一一個實例,整個IOC容器範圍內都能共享該實例:所有後續的getBean()調用和bean引用都將返回這個唯一的bean實例。該作用域被稱為singleton,它是所有bean的默認作用域。也就是單實例。
為了驗證這一說法,我們在IOC中創建一個單實例的bean,並且獲取該bean對象進行對比:
<!-- singleton單實例bean 1、在容器創建時被創建 2、只有一個實例 --> <bean id="book02" class="com.spring.beans.Book" scope="singleton"></bean>
測試獲取到的單實例bean是否是同一個:
@Test public void test09() { // 單實例創建時創建的兩個bean相等 Book book03 = (Book)iocContext3.getBean("book02"); Book book04 = (Book)iocContext3.getBean("book02"); System.out.println(book03==book04); }
得到的結果是true;
2、多實例Bean聲明
而既然存在單實例,那麼就一定存在多實例。我們可以為bean對象的scope屬性設置prototype參數,以表示該實例是多實例的,同時獲取IOC容器中的多實例bean,再將獲取到的多實例bean進行對比,
<!-- prototype多實例bean 1、在容器創建時不會被創建, 2、只有在被調用的時候才會被創建 3、可以存在多個實例 --> <bean id="book01" class="com.spring.beans.Book" scope="prototype"></bean>
測試獲取到的多實例bean是否是同一個:
@Test public void test09() { // 多實例創建時,創建的兩個bean對象不相等 Book book01 = (Book)iocContext3.getBean("book01"); Book book02 = (Book)iocContext3.getBean("book01"); System.out.println(book01==book02); }
得到的結果是false
這就說明了,通過多實例創建的bean對象是各不相同的。
在這裡需要注意:
同時關於單實例和多實例bean的創建也有不同,當bean的作用域為單例時,Spring會在IOC容器對象創建時就創建bean的對象實例。而當bean的作用域為prototype時,IOC容器在獲取bean的實例時創建bean的實例對象。
二、Bean的生命周期
1、bean的初始和銷毀
其實我們在IOC中創建的每一個bean對象都是有其特定的生命周期的,在Spring的IOC容器中可以管理bean的生命周期,Spring允許在bean生命周期內特定的時間點執行指定的任務。如在bean初始化時執行的方法和bean被銷毀時執行的方法。
Spring IOC容器對bean的生命周期進行管理的過程可以分為六步:
- 通過構造器或工廠方法創建bean實例
- 為bean的屬性設置值和對其他bean的引用
- 調用bean的初始化方法
- bean可以正常使用
- 當容器關閉時,調用bean的銷毀方法
那麼關於bean的初始和銷毀時執行的方法又該如何聲明呢?
首先我們應該在bean類內部添加初始和銷毀時執行的方法。如下面這個javabean:
package com.spring.beans; public class Book { private String bookName; private String author; /** * 初始化方法 * */ public void myInit() { System.out.println("book bean被創建"); } /** * 銷毀時方法 * */ public void myDestory() { System.out.println("book bean被銷毀"); } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @Override public String toString() { return "Book [bookName=" + bookName + ", author=" + author + "]"; } }
這時我們在配置bean時,可以通過init-method和destroy-method 屬性為bean指定初始化和銷毀方法,
<!-- 設置bean的生命周期 destory-method:結束調用的方法 init-method:起始時調用的方法 --> <bean id="book01" class="com.spring.beans.Book" destroy-method="myDestory" init-method="myInit"></bean>
這樣當我們在通過IOC容器創建和銷毀bean對象時就會執行相應的方法,
但是這裡還是有一點需要注意:
我們上面說了,單實例的bean和多實例的bean的創建時間是不同的,那麼他們的初始方法和銷毀方法的執行時間就稍稍有不同。
- 單實例下 bean的生命周期
容器啟動——>初始化方法——>(容器關閉)銷毀方法
- 多實例下 bean的生命周期
容器啟動——>調用bean——>初始化方法——>容器關閉(銷毀方法不執行)
2、bean的後置處理器
什麼是bean的後置處理器?bean後置處理器允許在調用初始化方法前後對bean進行額外的處理
bean後置處理器對IOC容器里的所有bean實例逐一處理,而非單一實例。
其典型應用是:檢查bean屬性的正確性或根據特定的標準更改bean的屬性。
bean後置處理器使用時需要實現接口:
org.springframework.beans.factory.config.BeanPostProcessor。
在初始化方法被調用前後,Spring將把每個bean實例分別傳遞給上述接口的以下兩個方法:
postProcessBeforeInitialization(Object, String)調用前
postProcessAfterInitialization(Object, String)調用後
如下是一個實現在該接口的後置處理器:
package com.spring.beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * 測試bean的後置處理器 * 在這裡要注意一點是為了出現bean和beanName,而不是arg0、arg1,需要綁定相應的源碼jar包 * */ public class MyBeanPostProcessor implements BeanPostProcessor{ /** * postProcessBeforeInitialization * 初始化方法執行前執行 * Object bean * String beanName xml容器中定義的bean名稱 * */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("【"+ beanName+"】初始化方法執行前..."); return bean; } /** * postProcessAfterInitialization * 初始化方法執行後執行 * Object bean * String beanName xml容器中定義的bean名稱 * */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("【"+ beanName+"】初始化方法執行後..."); return bean; } }
將該後置處理器加入到IOC容器中:
<!-- 測試bean的後置處理器 --> <bean id="beanPostProcessor" class="com.spring.beans.MyBeanPostProcessor"></bean>
由於現在我們的bean對象是單實例的,所以容器運行時就會直接創建bean對象,同時也會執行該bean的後置處理器方法和初始化方法,在容器被銷毀時又會執行銷毀方法。我們測試如下:
//*************************bean生命周期***************** // 由於ApplicationContext是一個頂層接口,裏面沒有銷毀方法close,所以需要使用它的子接口進行接收 ConfigurableApplicationContext iocContext01 = new ClassPathXmlApplicationContext("ioc1.xml"); @Test public void test01() { iocContext01.getBean("book01"); iocContext01.close(); }
運行結果:
總結一下後置處理器的執行過程:
- 通過構造器或工廠方法創建bean實例
- 為bean的屬性設置值和對其他bean的引用
- 將bean實例傳遞給bean後置處理器的postProcessBeforeInitialization()方法
- 調用bean的初始化方法
- 將bean實例傳遞給bean後置處理器的postProcessAfterInitialization()方法
- bean可以使用了
- 當容器關閉時調用bean的銷毀方法
所以添加bean後置處理器後bean的生命周期為:
容器啟動——後置處理器的before…——>初始化方法——>後置處理器的after…———>(容器關閉)銷毀方法