Spring Boot Bean生命周期

注:Spring Boot Bean的生命周期碎赢,什么是Bean的生命周期,就是Bean從創(chuàng)建到銷(xiāo)毀的過(guò)程辣之。

Bean的生命周期過(guò)程描述

我們先看一下Bean的生命周期過(guò)程中都會(huì)經(jīng)歷些什么僵驰,我先簡(jiǎn)單解釋一下,后面我們通過(guò)源碼進(jìn)行詳細(xì)解釋悍募。首先Spring在實(shí)例化Bean的時(shí)候,會(huì)先調(diào)用它的構(gòu)造函數(shù)洋机,進(jìn)行Bean的實(shí)例化坠宴,然后進(jìn)行Bean的初始化,Bean的初始化經(jīng)過(guò)三個(gè)階段初始化之前(applyBeanPostProcessorsBeforeInitialization)绷旗,其次是進(jìn)行初始化(invokeInitMethods)喜鼓,最后是初始化之后(postProcessAfterInitialization),這就是Bean的初始化過(guò)程衔肢;然后就開(kāi)始利用Bean進(jìn)行業(yè)務(wù)邏輯處理庄岖,最后容器正常關(guān)閉,Spring開(kāi)始銷(xiāo)毀Bean膀懈,Bean的銷(xiāo)毀過(guò)程相對(duì)比較簡(jiǎn)單,調(diào)用DisposableBeanAdapter.destroy()方法谨垃,該方法中有三個(gè)地方比較重要启搂,分別觸發(fā)Bean的生命周期方法,它們是:processor.postProcessBeforeDestruction(this.bean, this.beanName);
((DisposableBean) bean).destroy();
invokeCustomDestroyMethod(this.destroyMethod);

下面我把用圖片來(lái)進(jìn)行直觀(guān)展示:

Bean的生命周期.png

實(shí)例運(yùn)行

User類(lèi)刘陶,這里就是普通的一個(gè)Bean,用來(lái)添加到容器中胳赌,觀(guān)察其生命周期

package com.itbofeng.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class User implements InitializingBean , DisposableBean {
    private String name;
    public User() {
        System.out.println("調(diào)用Bean的函數(shù)(constructor)");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        System.out.println("調(diào)用Bean的函數(shù)(setName/setAttribute)");
        this.name = name;
    }
    @PostConstruct
    public void postConstruct(){
        System.out.println("調(diào)用Bean的函數(shù)(postConstruct)");
    }
    //MainConfig中@Bean 的initMethod
    public void initMethod(){
        System.out.println("調(diào)用Bean的函數(shù)(initMethod)");
    }
    //InitializingBean接口的方法afterPropertiesSet
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("調(diào)用Bean的函數(shù)(afterPropertiesSet)");
    }
    @PreDestroy
    public void preDestroy(){
        System.out.println("調(diào)用Bean的函數(shù)(preDestroy)");
    }
    //DisposableBean接口的方法destroy
    @Override
    public void destroy() throws Exception {
        System.out.println("調(diào)用Bean的函數(shù)(destroy)");
    }
    //MainConfig中@Bean 的destroyMethod
    public void destroyMethod(){
        System.out.println("調(diào)用Bean的函數(shù)(destroyMethod)");
    }
}

CustomBeanPostProcessor類(lèi),用來(lái)觀(guān)察BeanPostProcessor的運(yùn)行時(shí)期

package com.itbofeng.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Nullable
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean.getClass()==User.class){
            System.out.println("調(diào)用postProcessBeforeInitialization...");
        }
        return bean;
    }
    @Nullable
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean.getClass()==User.class){
            System.out.println("調(diào)用postProcessAfterInitialization...");
        }
        return bean;
    }
}

MainConfig類(lèi)匙隔,SpringBoot程序運(yùn)行的主類(lèi)疑苫,并將User和CustomBeanPostProcessor 加入到容器中

package com.itbofeng;
import com.itbofeng.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MainConfig {
    public static void main(String[] args) {
        SpringApplication.run(MainConfig.class,args);
    }
    @Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")
    public User user(){
        return new User();
    }
}

運(yùn)行結(jié)果查看


運(yùn)行結(jié)果.png

源碼解析

初始化過(guò)程
首先我們將斷點(diǎn)打到CustomBeanPostProcessor.postProcessBeforeInitialization的第一行代碼,然后我看一下其方法調(diào)用棧:


方法調(diào)用棧.png

主要包括兩個(gè)地方,一個(gè)是容器刷新捍掺,第二個(gè)是初始化Bean撼短,我們先對(duì)這容器刷新的源碼進(jìn)行簡(jiǎn)單查看

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

注意finishBeanFactoryInitialization,上面說(shuō)明也解釋說(shuō)該方法的作用是初始化所有剩下的非懶加載的單例Bean,從該描述也可以知道挺勿,懶加載和原型的Bean在該階段并不會(huì)被加載曲横,這部分代碼是Spring容器刷新時(shí)的代碼,也是Spring IOC比較核心的代碼不瓶,在學(xué)習(xí)后面中禾嫉,慢慢將該部分的代碼慢慢學(xué)習(xí),接下來(lái)我們看一下初始化Bean部分的代碼:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    //對(duì)實(shí)現(xiàn)了Aware接口的方法進(jìn)行執(zhí)行蚊丐,方法就在下方熙参,使用方法直接實(shí)現(xiàn)對(duì)應(yīng)的Aware接口即可
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        //執(zhí)行實(shí)現(xiàn)了BeanPostProcessor的postProcessBeforeInitialization方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        //執(zhí)行初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        //執(zhí)行實(shí)現(xiàn)了BeanPostProcessor的postProcessAfterInitialization方法
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        //調(diào)用InitializingBean的afterPropertiesSet方法
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    //調(diào)用@Bean制定的的initMethod方法
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

以上就是初始化階段,我們會(huì)發(fā)現(xiàn)初始化過(guò)程中沒(méi)有@PostConstruct注解標(biāo)注的方法麦备,那是因?yàn)閷?duì)@PostConstruct的處理也是BeanPostProcessor的實(shí)現(xiàn)類(lèi)InitDestroyAnnotationBeanPostProcessor孽椰,那么我們就應(yīng)該有疑問(wèn)為什么InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction為什么會(huì)在CustomBeanPostProcessor.postProcessBeforeInitialization之后進(jìn)行,是因?yàn)槿绻恢付樞蚰J(rèn)我們的getOrder為0泥兰,而InitDestroyAnnotationBeanPostProcessor的getOrder為Ordered.LOWEST_PRECEDENCE=2147483647,getOrder越大則弄屡,優(yōu)先級(jí)越低,所以我們自定義的在其之前鞋诗,至此初始化部分結(jié)束膀捷。
下面我們簡(jiǎn)單看一下銷(xiāo)毀階段:

public void destroy() {
    //處理@PreDestroy注解注釋的方法
    if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
        for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
            processor.postProcessBeforeDestruction(this.bean, this.beanName);
        }
    }
    //處理實(shí)現(xiàn)了DisposableBean接口的destroy的方法
    if (this.invokeDisposableBean) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
        }
        try {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((DisposableBean) bean).destroy();
                    return null;
                }, acc);
            }
            else {
                ((DisposableBean) bean).destroy();
            }
        }
        catch (Throwable ex) {
            String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
            if (logger.isDebugEnabled()) {
                logger.warn(msg, ex);
            }
            else {
                logger.warn(msg + ": " + ex);
            }
        }
    }
    //處理@Bean指定的destroyMethod方法
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToCall = determineDestroyMethod(this.destroyMethodName);
        if (methodToCall != null) {
            invokeCustomDestroyMethod(methodToCall);
        }
    }
}

結(jié)束語(yǔ)

至此,我們Bean的生命周期探索初步結(jié)束削彬,后面再做進(jìn)一步深入學(xué)習(xí)全庸,在學(xué)習(xí)本節(jié)課時(shí)要多進(jìn)行調(diào)試,斷點(diǎn)跟蹤融痛,會(huì)更加有效果壶笼。另外無(wú)論是在初始化還是在結(jié)束時(shí)都是處理的單例的Bean,而且在初始化時(shí),處理的Bean還是非懶加載的雁刷,所以需要特殊說(shuō)明:非懶加載的單實(shí)例Bean的生命周期如上;懶加載的單實(shí)例Bean是在第一次獲取進(jìn)行初始化過(guò)程;原型Bean是在每次獲取時(shí)都進(jìn)行初始化覆劈,而且Spring容器不會(huì)管理器銷(xiāo)毀。
最近在網(wǎng)上看到了一篇文章沛励,很不錯(cuò)责语,詳細(xì)的介紹了Spring IOC的原理,如果大家想詳細(xì)了解Spring的IOC可以看一下:https://www.cnblogs.com/ITtangtang/p/3978349.html目派,好東西大家一起分享坤候,????????????????????????????????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市企蹭,隨后出現(xiàn)的幾起案子白筹,更是在濱河造成了極大的恐慌智末,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徒河,死亡現(xiàn)場(chǎng)離奇詭異系馆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)虚青,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)它呀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人棒厘,你說(shuō)我怎么就攤上這事纵穿。” “怎么了奢人?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵谓媒,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我何乎,道長(zhǎng)句惯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任支救,我火速辦了婚禮抢野,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘各墨。我一直安慰自己指孤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布贬堵。 她就那樣靜靜地躺著恃轩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪黎做。 梳的紋絲不亂的頭發(fā)上叉跛,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音蒸殿,去河邊找鬼筷厘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宏所,可吹牛的內(nèi)容都是我干的酥艳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼楣铁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玖雁!你這毒婦竟也來(lái)了更扁?” 一聲冷哼從身側(cè)響起盖腕,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赫冬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后溃列,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體劲厌,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年听隐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了补鼻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雅任,死狀恐怖风范,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沪么,我是刑警寧澤硼婿,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站禽车,受9級(jí)特大地震影響寇漫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殉摔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一州胳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逸月,春花似錦栓撞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肛响,卻和暖如春岭粤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背特笋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工剃浇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猎物。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓虎囚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蔫磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淘讥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,713評(píng)論 2 22
  • 1.1 Spring IoC容器和bean簡(jiǎn)介 本章介紹了Spring Framework實(shí)現(xiàn)的控制反轉(zhuǎn)(IoC)...
    起名真是難閱讀 2,583評(píng)論 0 8
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理堤如,服務(wù)發(fā)現(xiàn)蒲列,斷路器窒朋,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,810評(píng)論 6 342
  • 在深圳樸門(mén)看狗2014-8-28 (早幾天回來(lái)時(shí)寫(xiě)的,存在電腦里蝗岖,忘了發(fā)出來(lái)侥猩。今兒發(fā)出來(lái)分享吧。) 清 早起來(lái)抵赢,妞...
    阿緣閱讀 134評(píng)論 0 0