面試官:瞭解Bean的生命週期嗎?說說Spring框架中Bean的生命週期

推薦閱讀

211本非科班,面試阿里、字節、快手、美團面經總結(已拿offer)

Java程序員掌握了被吹的神乎其神的微服務,真的能進阿里嗎? 

字節跳動Java崗算法面試有多難?看完這些你就知道了

最新阿里P6-P7Java研發崗面經:技能要求+面試真題+經驗總結!

前言

面試過程中被問到Bean的生命週期是很正常的,相信很多人都能答上來,但是放到spring框架裏面,難度就提升了很多,今天就來講一講Spring框架中Bean的聲明週期

一、Bean的生命週期

關於Bean的生命週期,如果我們不談這個Spring的話,實際上很多人都會想到New,通過 New 對象的形式來實現對 Bean的實例化操作,而在我們不再使用 Bean 了之後,這時候我們的 Java 就會對這個指定的 Bean 來進行垃圾回收了。

但是對於Spring來說,Bean的生命週期可能就比較讓人頭疼了,畢竟 Spring 這麼複雜,而且裏面的對 Bean 管理的非常的有邏輯了,每一層都有每一層的步驟。

如果現在我們去百度上面去搜索所有的關於Spring的Bean的生命週期,很多人會把這個解釋出來

  • 在IoC容器啓動之後,並不會馬上就實例化相應的bean,此時容器僅僅擁有所有對象的BeanDefinition(BeanDefinition:是容器依賴某些工具加載的XML配置信息進行解析和分析,並將分析後的信息編組爲相應的BeanDefinition)。只有當getBean()調用時纔是有可能觸發Bean實例化階段的活動

而有一些內容就不會說解釋的很透徹,比如說爲什麼說只有當 getBean() 調用的時候纔有可能觸發Bean的實例化。

二、生命週期流程圖

簡化版圖解

面試官:瞭解Bean的生命週期嗎?說說Spring框架中Bean的生命週期

 

而這圖解中,把 Spring 中 Bean 的生命週期分成了好幾個步驟,分別是:

  1. 通過構造方法實例化 Bean 對象。
  2. 通過 setter 方法設置對象的屬性。
  3. 通過Aware,也就是他的子類BeanNameAware,調用Bean的setBeanName()方法傳遞Bean的ID(XML裏面註冊的ID),setBeanName方法是在bean初始化時調用的,通過這個方法可以得到BeanFactory和 Bean 在 XML 裏面註冊的ID。
  4. 如果說 Bean 實現了 BeanFactoryAware,那麼工廠調用setBeanFactory(BeanFactory var1) 傳入的參數也是自身。
  5. 把 Bean 實例傳遞給 BeanPostProcessor 中的 postProcessBeforeInitialization 前置方法。
  6. 完成 Bean 的初始化
  7. 把 Bean 實例傳遞給 BeanPostProcessor 中的 postProcessAfterInitialization 後置方法。
  8. 此時 Bean 已經能夠正常時候,在最後的時候調用 DisposableBean 中的 destroy 方法進行銷燬處理。

如果面試官在面試的時候問到這個問題的時候,你從圖解開始入手,然後把這些都說給他之後,那麼相對應的,這現在這些答案,如果不繼續的深挖內容,可能已經就足夠了。

而接下來還要從根本上來論證上面所寫的內容。

而我們對這詳細的可能有時候難以記憶,可能還是理解不深,而我們可以從四到五個方面來記憶,

  • 構造實例化
  • 屬性賦值
  • 完成初始化
  • (前後處理)
  • 使用後銷燬

而從這五個方面來記憶,或許就能把這個圖擴展開,從而言簡意賅的回答面試官的問題。

代碼驗證

package com.yld.bean; 
 
import org.springframework.beans.factory.BeanNameAware; 
 
public class Person implements BeanNameAware { 
 
    private String name; 
 
    /** 
     * 實現類上的override方法 
     * @param s 
     */ 
    @Override 
    public void setBeanName(String s) { 
        System.out.println("調用BeanNameAware中的setName賦值"); 
    } 
 
    public Person() { 
    } 
 
    /** 
     * 屬性賦值 
     * @param name 
     */ 
    public void setName(String name) { 
        System.out.println("設置對象屬性setName().."); 
        this.name = name; 
    } 
 
    /** 
     * Bean初始化 
     */ 
    public void initBeanPerson() { 
        System.out.println("初始化Bean"); 
    } 
 
    /** 
     * Bean方法使用:說話 
     */ 
    public void speak() { 
        System.out.println("使用Bean的Speak方法"); 
    } 
 
    /** 
     * 銷燬Bean 
     */ 
    public void destroyBeanPerson() { 
        System.out.println("銷燬Bean"); 
    } 
 
 
} 

Main方法

public static void main(String[] args) { 
        ClassPathXmlApplicationContext pathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); 
        Person person = (Person)pathXmlApplicationContext.getBean("person"); 
        person.speak(); 
        pathXmlApplicationContext.close(); 
    } 

運行結果展示

D:\develop\JDK8\jdk1.8.0_181\bin\java.exe "-javaagent:D:\develop\IDEA\IntelliJ IDEA 2018.1.8\lib\idea_rt.jar=63906:D:\develop\IDEA\IntelliJ IDEA 2018.1.8\bin" -Dfile.encoding=UTF-8 -classpath D:\develop\JDK8\jdk1.8.0_181\jre\lib\charsets.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\deploy.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\javaws.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jce.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfr.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\jsse.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\management-agent.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\plugin.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\resources.jar;D:\develop\JDK8\jdk1.8.0_181\jre\lib\rt.jar;D:\develop\IDEAProject\KaiYuan\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\2.1.8.RELEASE\spring-boot-starter-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.1.8.RELEASE\spring-boot-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\5.1.9.RELEASE\spring-context-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\5.1.9.RELEASE\spring-aop-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\5.1.9.RELEASE\spring-beans-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\5.1.9.RELEASE\spring-expression-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.8.RELEASE\spring-boot-autoconfigure-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.8.RELEASE\spring-boot-starter-logging-2.1.8.RELEASE.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.28\jul-to-slf4j-1.7.28.jar;C:\Users\Administrator\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\5.1.9.RELEASE\spring-core-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\5.1.9.RELEASE\spring-jcl-5.1.9.RELEASE.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.28\slf4j-api-1.7.28.jar com.yld.bean.Test 
16:54:58.817 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@123772c4 
16:54:59.074 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [applicationContext.xml] 
16:54:59.121 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person' 
 
設置對象屬性setName().. 
 
調用BeanNameAware中的setName賦值 
 
初始化Bean 
 
使用Bean的Speak方法 
 
16:54:59.232 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@123772c4, started on Sun Jun 07 16:54:58 CST 2020 
 
銷燬Bean 
 
Process finished with exit code 0 

和大家預想的是不是一樣的呢? 在用案例回答面試官之後,我們最好還是要研究一下源碼的部分,畢竟研究清楚了,會理解的更深刻不是麼?

InstantiationAwareBeanPostProcessor

這個類是繼承的 BeanPostProcessor 而這個類的作用是什麼呢?源碼註釋解釋的是這樣子的:

方法一:

@Nullable 
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { 
        return null; 
    } 
應用這個Bean處理器在目標Bean實例化之前。返回的bean對象可能是一個代理bean的使用而不是目標, 

也就是說
postProcessBeforeInstantiation在bean實例化之前調用的,這是不是也是我們在面試中另外的一個面試點 AOP 的使用呢?到時候面試官讓你舉例子的時候,你直接用這個 Spring 裏面的源碼給他解釋,分分鐘讓面試官對你刮目想看呀有木有。

方法二:可以看到該方法在屬性賦值方法內,但是在真正執行賦值操作之前。其返回值爲boolean。

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { 
        return true; 
    } 

大家是不是還可以這麼理解,如果返回值爲false的話,那麼就出現了賦值失敗,也就是間接阻斷賦值了。

而初始化的類同樣的 BeanPostProcessor

方法一:

任何Bean之前初始化回調如初始化Bean的屬性設置後 
@Nullable 
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
        return bean; 
    } 

方法二:

應用這個Bean後置處理程序給定新的Bean實例,任何Bean初始化後回調(如初始化Bean的屬性設置後{@code}或一個自定義的init方法)。bean已經填充屬性值。返回的bean實例可能是原始的包裝器。

應用這個Bean後置處理程序給定新的Bean實例,任何Bean初始化後回調(如初始化Bean的屬性設置後{@code}或一個自定義的init方法)。bean已經填充屬性值。返回的bean實例可能是原始的包裝器。 
@Nullable 
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
        return bean; 
    } 

同樣註釋翻譯出來的意思也是很明確的,這也是博主爲什麼喜歡自己下載個插件去看註釋,畢竟源碼這個東西如果看別人理解的和自己理解的,有時候差距也是很大的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章