天天看點

面試官:了解Bean的生命周期嗎?說說Spring架構中Bean的生命周期

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

前言

面試過程中被問到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 的生命周期分成了好幾個步驟,分别是:

通過構造方法執行個體化 Bean 對象。

通過 setter 方法設定對象的屬性。

通過Aware,也就是他的子類BeanNameAware,調用Bean的setBeanName()方法傳遞Bean的ID(XML裡面注冊的ID),setBeanName方法是在bean初始化時調用的,通過這個方法可以得到BeanFactory和 Bean 在 XML 裡面注冊的ID。

如果說 Bean 實作了 BeanFactoryAware,那麼工廠調用setBeanFactory(BeanFactory var1) 傳入的參數也是自身。把 Bean 執行個體傳遞給 BeanPostProcessor 中的 postProcessBeforeInitialization 前置方法。

完成 Bean 的初始化把 Bean 執行個體傳遞給 BeanPostProcessor 中的 postProcessAfterInitialization 後置方法。

此時 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執行個體可能是原始的包裝器。

@Nullable     default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {         return bean;     }           

同樣注釋翻譯出來的意思也是很明确的,這也是部落客為什麼喜歡自己下載下傳個插件去看注釋,畢竟源碼這個東西如果看别人了解的和自己了解的,有時候差距也是很大的。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-06-30

本文作者:Java架構師小馬哥

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”