面試官:小伙子羡铲,你說一下java 對象創(chuàng)建和 Spring Bean 的生命周期吧

理解對象和Bean的關(guān)系

java 是一種面向?qū)ο蟮恼Z言,簡而言之儡毕,一切皆對象也切。Bean自然也是對象,只不過它是托管給 Bean 工廠管理著的對象妥曲。

java 對象如何被創(chuàng)建

在寫代碼時贾费,我們通常用下面的語句來創(chuàng)建一個對象:

A a=new A();

那么在創(chuàng)建對象的過程中,究竟發(fā)生了什么呢檐盟。其實上面簡單的一句話,在程序中發(fā)生了很多很多的事情押桃。
首先葵萎,一個對象是需要內(nèi)存去存放的。所以會有一個分配內(nèi)存的過程唱凯。分配了內(nèi)存之后羡忘,jvm便會開始創(chuàng)建對象,并將它賦值給 a 變量磕昼。然后再去初始化A中的一些屬性卷雕,并執(zhí)行A的構(gòu)造方法。在初始化的過程中票从,會先執(zhí)行 static 代碼塊,再執(zhí)行構(gòu)造方法漫雕。除此之外滨嘱,如果有父類,會優(yōu)先父類的進行執(zhí)行浸间。大致如下圖所示太雨。


1.25.2.png

如何驗證對象初始化的過程呢?用下面一段代碼驗證魁蒜。這段代碼很簡單囊扳,有靜態(tài)變量的初始化,有構(gòu)造方法兜看,有繼承锥咸。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InitTest {
    private static final Logger logger = LoggerFactory.getLogger(InitTest.class);
    // 1.靜態(tài)變量初始化
    static String staticWord = "hello";
    // 2.靜態(tài)代碼塊
    static  {
        logger.info("staticWord = "+staticWord);
    }
    public InitTest(){
        logger.info("father construct method invoke...");
    }
    public static void main(String[] args) {
        new Son();
    }
    static class Son extends InitTest{
        static  {
            logger.info("son staticWord init in static");
        }
        public Son(){
            logger.info("son construct method invoke...");
        }
    }//歡迎加入Java開發(fā)交流君樣:909038429
}

運行打印的日志如下,通過分析日志细移,我們可以得出她君,靜態(tài)代碼塊先于構(gòu)造方法,父類先于子類的執(zhí)行順序葫哗。

00:55:18.869 [main] INFO com.fc.study.InitTest - staticWord = hello
00:55:18.877 [main] INFO com.fc.study.InitTest - son staticWord init in static
00:55:18.877 [main] INFO com.fc.study.InitTest - father construct method invoke...
00:55:18.877 [main] INFO com.fc.study.InitTest - son construct method invoke...

Spring Bean 的生命周期

好的缔刹,有了對象的初始化順序,我們就可以繼續(xù)分析 bean 的生命周期了劣针。我們可以先回憶一下自己平時是怎么定義一個 bean的校镐。

@Component
public class TestBean{
}
@Bean
public Object myObject(){
}

常用的是上面這兩種:第一種是通過Component注解標注類;第二中方式是在方法上做@Bean的注解捺典。我們都知道鸟廓,注解標注的方法或者類,便會被spring掃描襟己,并最終生成一個bean引谜。本文不詳細討論bean掃描的過程,只分析bean初始化過程中的一些接口擎浴。
那么员咽,Spring 創(chuàng)建 Bean 就可以分為兩大步驟,第一步是由Springboot 掃描并獲取BeanDefinition贮预;第二部贝室,是初始化Bean。spring 在bean的初始化過程為我們提供了很多的接口仿吞,我們可以用它們在bean的生成過程中做一些事情滑频。這些接口均采用回調(diào)的方式,以下是部分接口的介紹和回調(diào)時機唤冈。


1.25.1.png

如果將上面的接口加入峡迷,則 bean 生命周期大致如下圖:


1.25.3.png

同樣,我們用代碼來驗證一下這個回調(diào)順序你虹。用來測試的Bean代碼如下绘搞,這個測試 bean 沒有繼承其他父類彤避,僅用來驗證springboot的接口在bean生命周期的調(diào)用時機:

package com.fc.study.beanLife;

import com.fc.study.InitTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class TestBean implements BeanNameAware, InitializingBean, ApplicationContextAware {
    private static final Logger logger = LoggerFactory.getLogger(InitTest.class);
    private String beanName;
    static String staticWord;
    static  {
        logger.info("father staticWord init in static");
        staticWord="hi";
    }
    public TestBean(){
        logger.info("testBean construct method invoke...");
    }

    public void setBeanName(String name) {
        logger.info("setBeanName");
        this.beanName = name;
    }

    public void afterPropertiesSet() throws Exception {
        logger.info("afterProperties Set");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        logger.info("applictionContextAware");
    }
}

同時,我定義了一個BeanPostProcessor 如下:

package com.fc.study.beanLife;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private static Logger logger = LoggerFactory.getLogger(DefaultBeanPostProcessor.class);
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("testBean")) {
            logger.info(beanName + " postProcessBeforeInitialization 執(zhí)行");
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("testBean")) {
            logger.info(beanName + " postProcessAfterInitialization 執(zhí)行");
        }
        return bean;
    }//歡迎加入Java開發(fā)交流君樣:909038429

    private ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

接下來是啟動類:

package com.fc.study.beanLife;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.fc.study")
public class SimpleSpringBoot {

    private static final Logger logger = LoggerFactory.getLogger(SimpleSpringBoot.class);

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(SimpleSpringBoot.class);

        logger.info("before get Bean");
        context.getBean(TestBean.class);
        logger.info("after get bean");
    }
}

運行啟動類看杭,打印出日志如下:

2021-01-23 02:18:09,764  INFO InitTest:29 - father staticWord init in static
2021-01-23 02:18:09,768  INFO InitTest:34 - testBean construct method invoke...
2021-01-23 02:18:09,768  INFO InitTest:38 - setBeanName
2021-01-23 02:18:09,768  INFO InitTest:48 - applictionContextAware
2021-01-23 02:18:09,768  INFO DefaultBeanPostProcessor:27 - testBean postProcessBeforeInitialization 執(zhí)行
2021-01-23 02:18:09,768  INFO InitTest:44 - afterProperties Set
2021-01-23 02:18:09,768  INFO DefaultBeanPostProcessor:34 - testBean postProcessAfterInitialization 執(zhí)行
2021-01-23 02:18:11,449  INFO SimpleSpringBoot:24 - before get Bean
2021-01-23 02:18:11,449  INFO SimpleSpringBoot:26 - after get bean

看看這個日志忠藤,印證了圖二對各個接口調(diào)用時機結(jié)論。

總結(jié)

對象初始化楼雹,就是創(chuàng)建對象模孩,并且初始化其屬性的過程。首先是加載類文件贮缅,其次對象所需要的內(nèi)存榨咐。然后靜態(tài)代碼塊會被調(diào)用,最后是構(gòu)造方法谴供。
Spring Bean的初始化块茁,除了創(chuàng)建對象這些步驟之外,還在其中穿插了一些生命周期的接口桂肌。首先在類加載完成后数焊,會得到BeanDefinition,然后通過這個定義來初始化崎场,而不是直接通過加載后的類對象來生成對象佩耳。在靜態(tài)代碼塊和構(gòu)造方法中間,Spring提供了幾個Aware接口谭跨,如表格中的BeanNameAware和ApplicationContextAware干厚。在構(gòu)造方法調(diào)用結(jié)束,并且springboot給bean set了所有屬性之后螃宙,會調(diào)用Initializing接口和BeanPostProcessor蛮瞄。
以上,便是我理解的 spring bean 生命周期谆扎,它就是 spring 在幫我們初始化對象管理對象的過程中額外做了一些事情挂捅。

image

最新2020整理收集的一些高頻面試題(都整理成文檔),有很多干貨燕酷,包含mysql籍凝,netty,spring苗缩,線程,spring cloud声诸、jvm酱讶、源碼、算法等詳細講解彼乌,也有詳細的學習規(guī)劃圖泻肯,面試題整理等渊迁,需要獲取這些內(nèi)容的朋友請加Q君樣:909038429
/./*歡迎加入java交流Q君樣:909038429一起吹水聊天

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灶挟,隨后出現(xiàn)的幾起案子琉朽,更是在濱河造成了極大的恐慌,老刑警劉巖稚铣,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箱叁,死亡現(xiàn)場離奇詭異,居然都是意外死亡惕医,警方通過查閱死者的電腦和手機耕漱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抬伺,“玉大人螟够,你說我怎么就攤上這事∠康觯” “怎么了妓笙?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長能岩。 經(jīng)常有香客問我寞宫,道長,這世上最難降的妖魔是什么捧灰? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任淆九,我火速辦了婚禮,結(jié)果婚禮上毛俏,老公的妹妹穿的比我還像新娘炭庙。我一直安慰自己,他們只是感情好煌寇,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布焕蹄。 她就那樣靜靜地躺著,像睡著了一般阀溶。 火紅的嫁衣襯著肌膚如雪腻脏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天银锻,我揣著相機與錄音永品,去河邊找鬼。 笑死击纬,一個胖子當著我的面吹牛鼎姐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼炕桨,長吁一口氣:“原來是場噩夢啊……” “哼饭尝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起献宫,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钥平,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姊途,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涉瘾,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年吭净,在試婚紗的時候發(fā)現(xiàn)自己被綠了睡汹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡寂殉,死狀恐怖囚巴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情友扰,我是刑警寧澤彤叉,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站村怪,受9級特大地震影響秽浇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甚负,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一柬焕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梭域,春花似錦斑举、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至既穆,卻和暖如春赎懦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背幻工。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工励两, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囊颅。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓伐蒋,卻偏偏與公主長得像工三,于是被迫代替她去往敵國和親迁酸。 傳聞我的和親對象是個殘疾皇子先鱼,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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