說實話,面試這麼問Spring框架的問題,我快扛不住了

面試官:Spring Framework有用過吧?

小小白:用過(有些心虛,因爲Spring框架中內容太多了)。

面試官:在applicationgContext.xml文件中定義了一個bean,id爲authService,通過ApplicationContext實例對象的getBean方法獲取到這個bean,這個背後的實現原理是什麼?

小小白:(心想得謹慎回答,因爲可能會把自己帶進坑裏)Spring容器啓動的時候會解析applicationgContext.xml,將xml中定義的bean(如authService)解析成Spring內部的BeanDefinition,並以beanName(如authService)爲key,BeanDefinition(如authService相應的BeanDefinition)爲value存儲到DefaultListableBeanFactory中的beanDefinitionMap屬性中(其實它就是一個ConcurrentHashMap類型的屬性),同時將beanName存入beanDefinitionNames中(List類型),然後遍歷beanDefinitionNames中的beanName,進行bean的實例化並填充屬性,在實例化的過程中,如果有依賴沒有被實例化將先實例化其依賴,然後實例化本身,實例化完成後將實例存入單例bean的緩存中,當調用getBean方法時,到單例bean的緩存中查找,如果找到並經過轉換後返回這個實例(如AuthService的實例),之後就可以直接使用了。

面試官:說一下xml文件的解析過程?

小小白:代碼中指定要加載的xml文件後,Spring容器初始化的過程中,通過ResourceLoader接口實現類,例如ClassPathXmlApplicationContext,將xml文件路徑轉換成對應的Resource文件,例如ClassPathResource,然後通過DocumentLoader對Resource文件進行轉換,轉換成Document文件,接着通過DefaultBeanDefinitionDocumentReader對Document進行解析,並使用BeanDefinitionParserDelegate對元素進行解析,解析xml中bean定義的各個元素,存入BeanDefinition中。

面試官:那你再詳細說一下這個BeanDefinition是什麼?

小小白:一個對象的生命週期要想被Spring容器管理,那麼它的類信息必須先轉成Spring內部的數據結構,BeanDefinition就是Spring框架內部用來描述對象的類信息的數據結構。例如類名、scope、屬性、構造函數參數列表、依賴的bean、是否是單例類、是否是懶加載等,其實就是將Bean的定義信息存儲到這個BeanDefinition相應的屬性中,後面對Bean的操作就直接對BeanDefinition進行,例如拿到這個BeanDefinition後,可以根據裏面的類名、構造函數、構造函數參數,使用反射進行對象創建。BeanDefinition是一個接口,是一個抽象的定義,實際使用的是其實現類,如ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。BeanDefinition繼承了AttributeAccessor,說明它具有處理屬性的能力;BeanDefinition繼承了BeanMetadataElement,說明它可以持有Bean元數據元素,作用是可以持有XML文件的一個bean標籤對應的Object。

面試官:剛剛你有說到DefaultListableBeanFactory,它在Spring框架中的作用是什麼?

小小白:DefaultListableBeanFactory是整個Bean加載的核心部分,是Spring註冊及加載Bean的默認實現。DefaultListableBeanFactory間接實現了BeanFactory接口,而在BeanFactory接口中定義了很多和bean操作相關的方法,例如getBean、containsBean、isSingleton等,所以DefaultListableBeanFactory也相應持有了這些操作。

面試官:那BeanFactory又是什麼?

小小白:BeanFactory是用於訪問Spring Bean容器的根接口,是一個單純的Bean工廠,也就是常說的ioc容器的頂層定義,各種ioc容器是在其基礎上爲了滿足不同需求而擴展的,包括經常使用的ApplicationContext。

面試官:如何理解BeanFactory和FactoryBean?

小小白:BeanFactory定義了ioc容器的最基本形式,並提供了ioc容器應遵守的的最基本的接口,也就是Spring ioc所遵守的最底層和最基本的編程規範,它只是個接口,並不是ioc容器的具體實現。它的職責包括:實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。再來說說FactoryBean,一般情況下,Spring通過反射機制利用bean的class屬性實例化Bean,然而在某些情況下,實例化Bean過程比較複雜,如果按照傳統的方式,則需要在bean的定義中提供大量的配置信息,而配置這種方式的靈活性是受限的,這時採用編碼的方式可能會是一個比較合適的方案,Spring爲此提供了FactoryBean的工廠類接口,用戶可以通過實現該接口定製實例化Bean的邏輯。

面試官:如果想在初始化前修改bean的屬性,如何實現?

小小白:自定義一個BeanFactoryPostProcessor,讓它實現BeanFactoryPostProcessor接口,並實現postProcessBeanFactory方法,在這個方法中可以在初始化前修改bean的屬性。

面試官:這個自定義的BeanFactoryPostProcessor是如何自動調用的?

小小白:在Spring容器初始化的過程中會自動觸發,具體代碼在AbstractApplicationContext類中會調用invokeBeanFactoryPostProcessors方法,在這個方法中篩選出所有實現BeanFactoryPostProcessor接口的類名稱,然後遍歷調用這些實現類的postProcessBeanFactory方法。

面試官:如果想在bean被初始化時進行攔截,進行額外初始化操作,如何實現?

小小白:自定義BeanPostProcessor,讓它實現BeanPostProcessor接口,在這個接口中定義了兩個方法:postProcessBeforeInitialization和postProcessAfterInitialization。postProcessBeforeInitialization方法會在afterPropertiesSet和自定義的初始化方法之前執行,通過實現這個方法,在方法的內部進行初始化之前的額外操作。postProcessAfterInitialization方法會在afterPropertiesSet和自定義的初始化方法之後執行,通過實現這個方法,在方法的內部進行初始化之後的額外操作。

面試官:在Spring容器初始化的過程中,所有定義的bean都會被初始化嗎?

小小白:不是,默認只初始化所有未初始化的非懶加載的單例Bean,scope爲其它值的bean會在使用到的時候進行初始化,如prototype。

面試官:有看過Spring中bean初始化的源碼嗎?

小小白:看過,單例bean的初始化,通過反射進行實例對象的創建,在進行屬性填充時,如果依賴的對象沒有創建,則先創建依賴對象,最後將bean實例加入單例bean實例的緩存中。

面試官:在bean實例化的過程中,Spring是如何解決循環依賴的?

小小白:Spring只對單例bean的循環依賴進行了解決,同時如果是通過構造函數注入造成的循環依賴,Spring也沒有辦法解決,只是拋出BeanCurrentlyInCreationException異常。如果是通過setter方式注入而產生的循環依賴,Spring在創建bean對象時,通過提前暴露一個ObjectFactory用來返回一個創建中的bean對象,從而使其它bean能夠引用到這個bean。

面試官:Spring框架中用到了哪些設計模式?

小小白:......額.....


推薦閱讀

沒使用加號拼接字符串,面試官竟然問我爲什麼

面試官一步一步的套路你,爲什麼SimpleDateFormat不是線程安全的

都說ThreadLocal被面試官問爛了,可爲什麼面試官還是喜歡繼續問

Java註解是如何玩轉的,面試官和我聊了半個小時

如何去除代碼中的多次if而引發的一連串面試問題

三分鐘快速搞定git常規使用

String引發的提問,我差點跪了

就寫了一行代碼,被問了這麼多問題

面試官:JVM對鎖進行了優化,都優化了啥?

synchronized連環問

深入Spring Boot (十三):整合Kafka詳解

深入理解Spring系列之十五:@Async實現原理

點點"在看" 唄

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