
面試官: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架構中用到了哪些設計模式?
小小白:......額.....