Spring Bean的生命周期

Spring作為當(dāng)前Java最流行、最強(qiáng)大的輕量級(jí)框架奋蔚,受到了程序員的熱烈歡迎窥浪。準(zhǔn)確的了解Spring Bean的生命周期是非常必要的。我們通常使用ApplicationContext作為Spring容器愿险。這里颇蜡,我們講的也是 ApplicationContext中Bean的生命周期。而實(shí)際上BeanFactory也是差不多的辆亏,只不過處理器需要手動(dòng)注冊(cè)风秤。

1、生命周期流程圖

Spring Bean的完整生命周期從創(chuàng)建Spring容器開始扮叨,直到最終Spring容器銷毀Bean缤弦,這其中包含了一系列關(guān)鍵點(diǎn)。


image.png

若容器注冊(cè)了以上各種接口彻磁,程序那么將會(huì)按照以上的流程進(jìn)行碍沐。下面將仔細(xì)講解各接口作用。

2衷蜓、實(shí)現(xiàn)過程詳解

2.1. 初始化

  • BeanNameAware.setBeanName() 在創(chuàng)建此bean的bean工廠中設(shè)置bean的名稱累提,在普通屬性設(shè)置之后調(diào)用,在InitializinngBean.afterPropertiesSet()方法之前調(diào)用

  • BeanClassLoaderAware.setBeanClassLoader(): 在普通屬性設(shè)置之后磁浇,InitializingBean.afterPropertiesSet()之前調(diào)用

  • BeanFactoryAware.setBeanFactory() : 回調(diào)提供了自己的bean實(shí)例工廠斋陪,在普通屬性設(shè)置之后,在InitializingBean.afterPropertiesSet()或者自定義初始化方法之前調(diào)用

  • EnvironmentAware.setEnvironment(): 設(shè)置environment在組件使用時(shí)調(diào)用

  • EmbeddedValueResolverAware.setEmbeddedValueResolver(): 設(shè)置StringValueResolver 用來解決嵌入式的值域問題

  • ResourceLoaderAware.setResourceLoader(): 在普通bean對(duì)象之后調(diào)用置吓,在afterPropertiesSet 或者自定義的init-method 之前調(diào)用无虚,在 ApplicationContextAware 之前調(diào)用。

  • ApplicationEventPublisherAware.setApplicationEventPublisher(): 在普通bean屬性之后調(diào)用衍锚,在初始化調(diào)用afterPropertiesSet 或者自定義初始化方法之前調(diào)用友题。在 ApplicationContextAware 之前調(diào)用。

  • MessageSourceAware.setMessageSource(): 在普通bean屬性之后調(diào)用戴质,在初始化調(diào)用afterPropertiesSet 或者自定義初始化方法之前調(diào)用度宦,在 ApplicationContextAware 之前調(diào)用。

  • ApplicationContextAware.setApplicationContext(): 在普通Bean對(duì)象生成之后調(diào)用置森,在InitializingBean.afterPropertiesSet之前調(diào)用或者用戶自定義初始化方法之前斗埂。在ResourceLoaderAware.setResourceLoader,ApplicationEventPublisherAware.setApplicationEventPublisher凫海,MessageSourceAware之后調(diào)用呛凶。

  • ServletContextAware.setServletContext(): 運(yùn)行時(shí)設(shè)置ServletContext,在普通bean初始化后調(diào)用行贪,在InitializingBean.afterPropertiesSet之前調(diào)用漾稀,在 ApplicationContextAware 之后調(diào)用注:是在WebApplicationContext 運(yùn)行時(shí)

  • BeanPostProcessor.postProcessBeforeInitialization() : 將此BeanPostProcessor 應(yīng)用于給定的新bean實(shí)例 在任何bean初始化回調(diào)方法(像是InitializingBean.afterPropertiesSet或者自定義的初始化方法)之前調(diào)用模闲。這個(gè)bean將要準(zhǔn)備填充屬性的值。返回的bean示例可能被普通對(duì)象包裝崭捍,默認(rèn)實(shí)現(xiàn)返回是一個(gè)bean尸折。

  • BeanPostProcessor.postProcessAfterInitialization() : 將此BeanPostProcessor 應(yīng)用于給定的新bean實(shí)例 在任何bean初始化回調(diào)方法(像是InitializingBean.afterPropertiesSet或者自定義的初始化方法)之后調(diào)用。這個(gè)bean將要準(zhǔn)備填充屬性的值殷蛇。返回的bean示例可能被普通對(duì)象包裝

  • InitializingBean.afterPropertiesSet(): 被BeanFactory在設(shè)置所有bean屬性之后調(diào)用(并且滿足BeanFactory 和 ApplicationContextAware)实夹。

2.2. 銷毀

  • 在BeanFactory 關(guān)閉的時(shí)候,Bean的生命周期會(huì)調(diào)用如下方法:
    DestructionAwareBeanPostProcessor.postProcessBeforeDestruction(): 在銷毀之前將此BeanPostProcessor 應(yīng)用于給定的bean實(shí)例粒梦。能夠調(diào)用自定義回調(diào)亮航,像是DisposableBean 的銷毀和自定義銷毀方法,這個(gè)回調(diào)僅僅適用于工廠中的單例bean(包括內(nèi)部bean)

  • 實(shí)現(xiàn)了自定義的destory()方法

3匀们、示例演示

為了驗(yàn)證Bean生命周期的過程缴淋,實(shí)例代碼如下:
Book.class:

public class Book implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean,BeanClassLoaderAware,
        EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,
        ApplicationEventPublisherAware,MessageSourceAware {

    private String bookName;
    public Book(){
        System.out.println("Book Initializing ");
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Book.setBeanFactory invoke");
    }

    public void setBeanName(String name) {
        System.out.println("Book.setBeanName invoke");
    }

    public void destroy() throws Exception {
        System.out.println("Book.destory invoke");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("Book.afterPropertiesSet invoke");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("Book.setApplicationContext invoke");
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
        System.out.println("setBookName: Book name has set.");
    }

    public void myPostConstruct(){
        System.out.println("Book.myPostConstruct invoke");
    }

    // 自定義初始化方法
    @PostConstruct
    public void springPostConstruct(){
        System.out.println("@PostConstruct");
    }

    public void myPreDestory(){
        System.out.println("Book.myPreDestory invoke");
        System.out.println("---------------destroy-----------------");
    }

    // 自定義銷毀方法
    @PreDestroy
    public void springPreDestory(){
        System.out.println("@PreDestory");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("------inside finalize-----");
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BookClass.setBeanClassLoader() 方法被調(diào)用了");
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("BookClass.setApplicationEventPublisher() 方法被調(diào)用了");
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        System.out.println("BookClass.setEmbeddedValueResolver() 方法被調(diào)用了");
    }

    public void setEnvironment(Environment environment) {
        System.out.println("BookClass.setEnvironment() 方法被調(diào)用了");
    }

    public void setMessageSource(MessageSource messageSource) {
        System.out.println("BookClass.setMessageSource() 方法被調(diào)用了");
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        System.out.println("BookClass.setResourceLoader() 方法被調(diào)用了");
    }

}

自定義實(shí)現(xiàn)BeanPostProcessor 的MyBeanPostProcessor

public class MyBeanPostProcessor implements BeanPostProcessor {

    // 容器加載的時(shí)候會(huì)加載一些其他的bean,會(huì)調(diào)用初始化前和初始化后方法
    // 這次只關(guān)注book(bean)的生命周期
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Book){
            System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization");
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Book){
            System.out.println("MyBeanPostProcessor.postProcessAfterInitialization");
        }
        return bean;
    }
}

在resources 目錄下新建Bean-Lifecycle.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

      <!-- 掃描bean -->
    <context:component-scan base-package="com.bean.lifecycle"/>

    <!-- 實(shí)現(xiàn)了用戶自定義初始化和銷毀方法 -->
    <bean id="book" class="com.bean.lifecycle.Book" init-method="myPostConstruct" destroy-method="myPreDestory">
          <!-- 注入bean 屬性名稱 -->
        <property name="bookName" value="thingking in java" />
    </bean>

      <!--引入自定義的BeanPostProcessor-->
    <bean class="com.bean.lifecycle.MyBeanPostProcessor"/>

</beans>

做一個(gè)啟動(dòng)類的測(cè)試泄朴,新建SpringBeanLifecycleApplication

public class SpringBeanLifecycleApplication {

    public static void main(String[] args) throws InterruptedException {
        // 簡(jiǎn)單版的Bean生命周期加載過程
        ApplicationContext context = new ClassPathXmlApplicationContext("Bean-Lifecycle.xml");
        Book book = (Book)context.getBean("book");
        System.out.println("Book name = " + book.getBookName());
        ((ClassPathXmlApplicationContext) context).destroy();
    }
}

啟動(dòng)測(cè)試重抖,輸出結(jié)果如下

Book Initializing 
setBookName: Book name has set.
Book.setBeanName invoke
BookClass.setBeanClassLoader() 方法被調(diào)用了
Book.setBeanFactory invoke
BookClass.setEnvironment() 方法被調(diào)用了
BookClass.setEmbeddedValueResolver() 方法被調(diào)用了
BookClass.setResourceLoader() 方法被調(diào)用了
BookClass.setApplicationEventPublisher() 方法被調(diào)用了
BookClass.setMessageSource() 方法被調(diào)用了
Book.setApplicationContext invoke
MyBeanPostProcessor.postProcessBeforeInitialization
@PostConstruct
Book.afterPropertiesSet invoke
Book.myPostConstruct invoke
MyBeanPostProcessor.postProcessAfterInitialization
Book name = thingking in java
@PreDestory
Book.destory invoke
Book.myPreDestory invoke
---------------destroy-----------------
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市祖灰,隨后出現(xiàn)的幾起案子钟沛,更是在濱河造成了極大的恐慌,老刑警劉巖局扶,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讹剔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡详民,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門陌兑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沈跨,“玉大人,你說我怎么就攤上這事兔综《隽荩” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵软驰,是天一觀的道長(zhǎng)涧窒。 經(jīng)常有香客問我,道長(zhǎng)锭亏,這世上最難降的妖魔是什么纠吴? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮慧瘤,結(jié)果婚禮上戴已,老公的妹妹穿的比我還像新娘固该。我一直安慰自己,他們只是感情好糖儡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布伐坏。 她就那樣靜靜地躺著,像睡著了一般握联。 火紅的嫁衣襯著肌膚如雪桦沉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天金闽,我揣著相機(jī)與錄音纯露,去河邊找鬼。 笑死呐矾,一個(gè)胖子當(dāng)著我的面吹牛苔埋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜒犯,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼组橄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了罚随?” 一聲冷哼從身側(cè)響起玉工,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淘菩,沒想到半個(gè)月后遵班,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潮改,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年狭郑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汇在。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翰萨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糕殉,到底是詐尸還是另有隱情亩鬼,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布阿蝶,位于F島的核電站雳锋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏羡洁。R本人自食惡果不足惜玷过,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冶匹,春花似錦习劫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至飞蛹,卻和暖如春谤狡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卧檐。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工墓懂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霉囚。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓捕仔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盈罐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子榜跌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359