Spring中Bean的作用域與生命周期詳解
一、Bean的作用域
首先我們來講一下有關(guān)于bean的作用域,
一般情況下,我們書寫在IOC容器中的配置信息,會(huì)在我們的IOC容器運(yùn)行時(shí)被創(chuàng)建,這就導(dǎo)致我們通過IOC容器獲取到bean對(duì)象的時(shí)候,往往都是獲取到了單實(shí)例的Bean對(duì)象,
這樣就意味著無論我們使用多少個(gè)getBean()方法,獲取到的同一個(gè)JavaBean都是同一個(gè)對(duì)象,這就是單實(shí)例Bean,整個(gè)項(xiàng)目都會(huì)共享這一個(gè)bean對(duì)象。
在Spring中,可以在<bean>元素的scope屬性里設(shè)置bean的作用域,以決定這個(gè)bean是單實(shí)例的還是多實(shí)例的。Scope屬性有四個(gè)參數(shù),具體的使用可以看下圖:
1、單實(shí)例Bean聲明
默認(rèn)情況下,Spring只為每個(gè)在IOC容器里聲明的bean創(chuàng)建唯一一個(gè)實(shí)例,整個(gè)IOC容器范圍內(nèi)都能共享該實(shí)例:所有后續(xù)的getBean()調(diào)用和bean引用都將返回這個(gè)唯一的bean實(shí)例。該作用域被稱為singleton,它是所有bean的默認(rèn)作用域。也就是單實(shí)例。
為了驗(yàn)證這一說法,我們?cè)贗OC中創(chuàng)建一個(gè)單實(shí)例的bean,并且獲取該bean對(duì)象進(jìn)行對(duì)比:
<!-- singleton單實(shí)例bean 1、在容器創(chuàng)建時(shí)被創(chuàng)建 2、只有一個(gè)實(shí)例 --> <bean id="book02" class="com.spring.beans.Book" scope="singleton"></bean>
測試獲取到的單實(shí)例bean是否是同一個(gè):
@Test public void test09() { // 單實(shí)例創(chuàng)建時(shí)創(chuàng)建的兩個(gè)bean相等 Book book03 = (Book)iocContext3.getBean("book02"); Book book04 = (Book)iocContext3.getBean("book02"); System.out.println(book03==book04); }
得到的結(jié)果是true;
2、多實(shí)例Bean聲明
而既然存在單實(shí)例,那么就一定存在多實(shí)例。我們可以為bean對(duì)象的scope屬性設(shè)置prototype參數(shù),以表示該實(shí)例是多實(shí)例的,同時(shí)獲取IOC容器中的多實(shí)例bean,再將獲取到的多實(shí)例bean進(jìn)行對(duì)比,
<!-- prototype多實(shí)例bean 1、在容器創(chuàng)建時(shí)不會(huì)被創(chuàng)建, 2、只有在被調(diào)用的時(shí)候才會(huì)被創(chuàng)建 3、可以存在多個(gè)實(shí)例 --> <bean id="book01" class="com.spring.beans.Book" scope="prototype"></bean>
測試獲取到的多實(shí)例bean是否是同一個(gè):
@Test public void test09() { // 多實(shí)例創(chuàng)建時(shí),創(chuàng)建的兩個(gè)bean對(duì)象不相等 Book book01 = (Book)iocContext3.getBean("book01"); Book book02 = (Book)iocContext3.getBean("book01"); System.out.println(book01==book02); }
得到的結(jié)果是false
這就說明了,通過多實(shí)例創(chuàng)建的bean對(duì)象是各不相同的。
在這里需要注意:
同時(shí)關(guān)于單實(shí)例和多實(shí)例bean的創(chuàng)建也有不同,當(dāng)bean的作用域?yàn)閱卫龝r(shí),Spring會(huì)在IOC容器對(duì)象創(chuàng)建時(shí)就創(chuàng)建bean的對(duì)象實(shí)例。而當(dāng)bean的作用域?yàn)閜rototype時(shí),IOC容器在獲取bean的實(shí)例時(shí)創(chuàng)建bean的實(shí)例對(duì)象。
二、Bean的生命周期
1、bean的初始和銷毀
其實(shí)我們?cè)贗OC中創(chuàng)建的每一個(gè)bean對(duì)象都是有其特定的生命周期的,在Spring的IOC容器中可以管理bean的生命周期,Spring允許在bean生命周期內(nèi)特定的時(shí)間點(diǎn)執(zhí)行指定的任務(wù)。如在bean初始化時(shí)執(zhí)行的方法和bean被銷毀時(shí)執(zhí)行的方法。
Spring IOC容器對(duì)bean的生命周期進(jìn)行管理的過程可以分為六步:
- 通過構(gòu)造器或工廠方法創(chuàng)建bean實(shí)例
- 為bean的屬性設(shè)置值和對(duì)其他bean的引用
- 調(diào)用bean的初始化方法
- bean可以正常使用
- 當(dāng)容器關(guān)閉時(shí),調(diào)用bean的銷毀方法
那么關(guān)于bean的初始和銷毀時(shí)執(zhí)行的方法又該如何聲明呢?
首先我們應(yīng)該在bean類內(nèi)部添加初始和銷毀時(shí)執(zhí)行的方法。如下面這個(gè)javabean:
package com.spring.beans; public class Book { private String bookName; private String author; /** * 初始化方法 * */ public void myInit() { System.out.println("book bean被創(chuàng)建"); } /** * 銷毀時(shí)方法 * */ 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 + "]"; } }
這時(shí)我們?cè)谂渲胋ean時(shí),可以通過init-method和destroy-method 屬性為bean指定初始化和銷毀方法,
<!-- 設(shè)置bean的生命周期 destory-method:結(jié)束調(diào)用的方法 init-method:起始時(shí)調(diào)用的方法 --> <bean id="book01" class="com.spring.beans.Book" destroy-method="myDestory" init-method="myInit"></bean>
這樣當(dāng)我們?cè)谕ㄟ^IOC容器創(chuàng)建和銷毀bean對(duì)象時(shí)就會(huì)執(zhí)行相應(yīng)的方法,
但是這里還是有一點(diǎn)需要注意:
我們上面說了,單實(shí)例的bean和多實(shí)例的bean的創(chuàng)建時(shí)間是不同的,那么他們的初始方法和銷毀方法的執(zhí)行時(shí)間就稍稍有不同。
單實(shí)例下 bean的生命周期
容器啟動(dòng)——>初始化方法——>(容器關(guān)閉)銷毀方法
多實(shí)例下 bean的生命周期
容器啟動(dòng)——>調(diào)用bean——>初始化方法——>容器關(guān)閉(銷毀方法不執(zhí)行)
2、bean的后置處理器
什么是bean的后置處理器?bean后置處理器允許在調(diào)用初始化方法前后對(duì)bean進(jìn)行額外的處理
bean后置處理器對(duì)IOC容器里的所有bean實(shí)例逐一處理,而非單一實(shí)例。
其典型應(yīng)用是:檢查bean屬性的正確性或根據(jù)特定的標(biāo)準(zhǔn)更改bean的屬性。
bean后置處理器使用時(shí)需要實(shí)現(xiàn)接口:
org.springframework.beans.factory.config.BeanPostProcessor。
在初始化方法被調(diào)用前后,Spring將把每個(gè)bean實(shí)例分別傳遞給上述接口的以下兩個(gè)方法:
postProcessBeforeInitialization(Object, String)調(diào)用前
postProcessAfterInitialization(Object, String)調(diào)用后
如下是一個(gè)實(shí)現(xiàn)在該接口的后置處理器:
package com.spring.beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * 測試bean的后置處理器 * 在這里要注意一點(diǎn)是為了出現(xiàn)bean和beanName,而不是arg0、arg1,需要綁定相應(yīng)的源碼jar包 * */ public class MyBeanPostProcessor implements BeanPostProcessor{ /** * postProcessBeforeInitialization * 初始化方法執(zhí)行前執(zhí)行 * 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+"】初始化方法執(zhí)行前..."); return bean; } /** * postProcessAfterInitialization * 初始化方法執(zhí)行后執(zhí)行 * 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+"】初始化方法執(zhí)行后..."); return bean; } }
將該后置處理器加入到IOC容器中:
<!-- 測試bean的后置處理器 --> <bean id="beanPostProcessor" class="com.spring.beans.MyBeanPostProcessor"></bean>
由于現(xiàn)在我們的bean對(duì)象是單實(shí)例的,所以容器運(yùn)行時(shí)就會(huì)直接創(chuàng)建bean對(duì)象,同時(shí)也會(huì)執(zhí)行該bean的后置處理器方法和初始化方法,在容器被銷毀時(shí)又會(huì)執(zhí)行銷毀方法。我們測試如下:
//*************************bean生命周期***************** // 由于ApplicationContext是一個(gè)頂層接口,里面沒有銷毀方法close,所以需要使用它的子接口進(jìn)行接收 ConfigurableApplicationContext iocContext01 = new ClassPathXmlApplicationContext("ioc1.xml"); @Test public void test01() { iocContext01.getBean("book01"); iocContext01.close(); }
運(yùn)行結(jié)果:
總結(jié)一下后置處理器的執(zhí)行過程:
通過構(gòu)造器或工廠方法創(chuàng)建bean實(shí)例為bean的屬性設(shè)置值和對(duì)其他bean的引用將bean實(shí)例傳遞給bean后置處理器的postProcessBeforeInitialization()方法調(diào)用bean的初始化方法將bean實(shí)例傳遞給bean后置處理器的postProcessAfterInitialization()方法bean可以使用了當(dāng)容器關(guān)閉時(shí)調(diào)用bean的銷毀方法
所以添加bean后置處理器后bean的生命周期為:
容器啟動(dòng)——后置處理器的before...——>初始化方法——>后置處理器的after...———>(容器關(guān)閉)銷毀方法
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot整合ElasticSearch實(shí)踐
本篇文章主要介紹了SpringBoot整合ElasticSearch實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05SpringMVC框架使用jackson封裝數(shù)據(jù)過程中遇到的問題及解決
這篇文章主要介紹了SpringMVC框架使用jackson封裝數(shù)據(jù)過程中遇到的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07Java Websocket Canvas實(shí)現(xiàn)井字棋網(wǎng)絡(luò)游戲
這篇文章主要介紹了Java Websocket Canvas實(shí)現(xiàn)井字棋網(wǎng)絡(luò)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08mybatis多對(duì)多關(guān)聯(lián)實(shí)戰(zhàn)教程(推薦)
下面小編就為大家?guī)硪黄猰ybatis多對(duì)多關(guān)聯(lián)實(shí)戰(zhàn)教程(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10JavaSE實(shí)戰(zhàn)之酒店訂房系統(tǒng)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了如何利用JavaSE實(shí)現(xiàn)酒店訂房系統(tǒng),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaSE開發(fā)有一定的幫助,需要的可以參考一下2022-07-07Java Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn)
本文主要介紹了Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解
這篇文章主要介紹了Java零基礎(chǔ)教程之Windows下安裝 JDK的方法圖解,本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09