Java面試題沖刺第十八天--Spring框架3
面試題1:Bean 的加載過(guò)程是怎樣的?
我們知道, Spring 的工作流主要包括以下兩個(gè)環(huán)節(jié):
- 解析,讀 xml 配置,掃描類文件,從配置或者注解中獲取 Bean 的定義信息,注冊(cè)一些擴(kuò)展功能。
- 加載,通過(guò)解析完的定義信息獲取 Bean 實(shí)例。
下面是跟蹤了 getBean的調(diào)用鏈創(chuàng)建的流程圖,為了能夠很好地理解 Bean 加載流程,省略一些異常、日志和分支處理和一些特殊條件的判斷。
從上面的流程圖中,可以看到一個(gè) Bean 加載主要會(huì)經(jīng)歷這么幾個(gè)階段(標(biāo)綠內(nèi)容):
- 獲取 BeanName,對(duì)傳入的 name 進(jìn)行解析,轉(zhuǎn)化為可以從 Map 中獲取到 BeanDefinition 的 bean name。
- 合并 Bean 定義,對(duì)父類的定義進(jìn)行合并和覆蓋,如果父類還有父類,會(huì)進(jìn)行遞歸合并,以獲取完整的 Bean 定義信息。
- 實(shí)例化,使用構(gòu)造或者工廠方法創(chuàng)建 Bean 實(shí)例。
- 屬性填充,尋找并且注入依賴,依賴的 Bean 還會(huì)遞歸調(diào)用 getBean 方法獲取。
- 初始化,調(diào)用自定義的初始化方法。
- 獲取最終的 Bean,如果是 FactoryBean 需要調(diào)用 getObject 方法,如果需要類型轉(zhuǎn)換調(diào)用 TypeConverter 進(jìn)行轉(zhuǎn)化。
以上便是Spring對(duì)bean解析注冊(cè)的全過(guò)程,總結(jié)一下大致步驟:
- 加載XML文件,封裝成Resource對(duì)象;
- 調(diào)用Reader對(duì)象方法讀取XML文件內(nèi)容,并將相關(guān)屬性放到BeanDefinition實(shí)例;
- 將BeanDefinition對(duì)象放到BeanFactory對(duì)象,用于調(diào)用;
追問(wèn)1:什么是循環(huán)依賴?
舉個(gè)例子,這里有三個(gè)類 A、B、C,然后 A 關(guān)聯(lián) B,B 關(guān)聯(lián) C,C 又關(guān)聯(lián) A,這就形成了一個(gè)循環(huán)依賴。如果是方法調(diào)用是不算循環(huán)依賴的,循環(huán)依賴必須要持有引用。
循環(huán)依賴發(fā)生的場(chǎng)景:
- 構(gòu)造器循環(huán)依賴:依賴的對(duì)象是通過(guò)構(gòu)造器傳入的,發(fā)生在實(shí)例化 Bean 的時(shí)候。
- 設(shè)值循環(huán)依賴:依賴的對(duì)象是通過(guò) setter 方法傳入的,對(duì)象已經(jīng)實(shí)例化,發(fā)生屬性填充和依賴注入的時(shí)候。
- 如果是構(gòu)造器循環(huán)依賴,本質(zhì)上是無(wú)法解決的。比如我們準(zhǔn)調(diào)用 A 的構(gòu)造器,發(fā)現(xiàn)依賴 B,于是去調(diào)用 B 的構(gòu)造器進(jìn)行實(shí)例化,發(fā)現(xiàn)又依賴 C,于是調(diào)用 C 的構(gòu)造器去初始化,結(jié)果依賴 A,整個(gè)形成一個(gè)死結(jié),導(dǎo)致 A 無(wú)法創(chuàng)建。
- 如果是設(shè)值循環(huán)依賴,Spring 框架只支持單例下的設(shè)值循環(huán)依賴。Spring 通過(guò)對(duì)還在創(chuàng)建過(guò)程中的單例,緩存并提前暴露該單例,使得其他實(shí)例可以引用該依賴。
追問(wèn)2:循環(huán)依賴得解決思路是什么樣的?
Spring解決循環(huán)依賴,主要的思路就是依據(jù)三級(jí)緩存(解鏈)。
在實(shí)例化A時(shí)調(diào)用doGetBean,發(fā)現(xiàn)A依賴的B的實(shí)例,此時(shí)調(diào)用doGetBean去實(shí)例B,實(shí)例化的B的時(shí)候發(fā)現(xiàn)又依賴A,如果不解決這個(gè)循環(huán)依賴的話此時(shí)的doGetBean將會(huì)無(wú)限循環(huán)下去,導(dǎo)致內(nèi)存溢出,程序奔潰。
如果Spring引用一個(gè)早期對(duì)象,并且把這個(gè)"早期引用"并將其注入到容器中,讓B先完成實(shí)例化,此時(shí)A就獲取B的引用,完成實(shí)例化。
一級(jí)緩存:singletonObjects,存放完全實(shí)例化屬性賦值完成的Bean,直接可以使用。
二級(jí)緩存:earlySingletonObjects,存放早期Bean的引用,尚未屬性裝配的Bean
三級(jí)緩存:singletonFactories,三級(jí)緩存,存放實(shí)例化完成的Bean工廠。
面試題2:@Resource和@Autowired有什么區(qū)別?
- @Autowired 根據(jù)類型注入
- @Resource 默認(rèn)根據(jù)名字注入,其次按照類型搜索
- @Autowired @Qualifie("userService") 兩個(gè)結(jié)合起來(lái)可以根據(jù)名字和類型注入,等同于@Resource
1.@Autowired與@Resource都可以用來(lái)裝配bean. 都可以寫在字段上,或?qū)懺趕etter方法上。
2.@Autowired默認(rèn)按類型裝配(byType),默認(rèn)情況下必須要求依賴對(duì)象必須存在,如果要允許null值,可以設(shè)置它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結(jié)合@Qualifier注解進(jìn)行使用(@Autowired () @Qualifier ( "xxx" )功能同@Resource),如下:
@Autowired @Qualifier ( "userDao" ) private UserDao userDao;
3.@Resource默認(rèn)按照名稱進(jìn)行裝配(byName),名稱可以通過(guò)name屬性進(jìn)行指定,如果沒(méi)有指定name屬性,當(dāng)注解寫在字段上時(shí),默認(rèn)取字段名進(jìn)行安裝名稱查找,如果注解寫在setter方法上默認(rèn)取屬性名進(jìn)行裝配。當(dāng)找不到與名稱匹配的bean時(shí)才按照類型進(jìn)行裝配。如果name屬性一旦指定,就只會(huì)按照名稱進(jìn)行裝配。
@Resource (name= "baseDao" ) private BaseDao baseDao;
總結(jié)如下:
- @Autowired默認(rèn)按byType自動(dòng)裝配,而@Resource默認(rèn)byName自動(dòng)裝配。
- @Autowired只包含一個(gè)參數(shù):required,表示是否開(kāi)啟自動(dòng)注入,默認(rèn)是true。而@Resource包含七個(gè)參數(shù),其中最重要的兩個(gè)參數(shù)是:name 和 type。
- @Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,則用byName自動(dòng)裝配,如果指定了type,則用byType自動(dòng)裝配。
- @Autowired能夠用在:構(gòu)造器、方法、參數(shù)、成員變量和注解上,而@Resource能用在:類、成員變量和方法上。
- @Autowired是spring定義的注解,而@Resource是JSR-250定義的注解。
面試題3:Spring 的事務(wù)傳播行為有哪些,都有什么作用?
簡(jiǎn)單來(lái)講,就是當(dāng)系統(tǒng)中存在兩個(gè)事務(wù)方法時(shí)(我們暫稱為方法A和方法B),如果方法B在方法A中被調(diào)用,那么將采用什么樣的事務(wù)形式,就叫做事務(wù)的傳播特性
比如,A方法調(diào)用了B方法(B方法必須使用事務(wù)注解),那么B事務(wù)可以是一個(gè)在A中嵌套的事務(wù),或者B事務(wù)不使用事務(wù),又或是使用與A事務(wù)相同的事務(wù),這些均可以通過(guò)指定事務(wù)傳播特性來(lái)實(shí)現(xiàn)。
傳播行為 | 意義 |
---|---|
propagation.REQUIRED | 表示當(dāng)前方法必須運(yùn)行在事務(wù)中 。如果當(dāng)前事務(wù)存在,方法將會(huì)在該事務(wù)中運(yùn)行。否則會(huì)啟動(dòng)一個(gè)新的事務(wù) |
propagation.SUPPORTS | 表示當(dāng)前方法不需要事務(wù)上下文 ,但是如果存在當(dāng)前事務(wù)的話,那么該方法會(huì)在這個(gè)事務(wù)中運(yùn)行 |
propagation.MANDATORY | 表示該方法必須在事務(wù)中運(yùn)行 ,如果當(dāng)前事務(wù)不存在,則會(huì)拋出一個(gè)異常 |
propagation.REQUIRED_NEW | 表示當(dāng)前方法必須運(yùn)行在它自己的事務(wù)中 。一個(gè)新的事務(wù)將被啟動(dòng)。如果存在當(dāng)前事務(wù),在該方法執(zhí)行期間,當(dāng)前事務(wù)會(huì)被掛起。如果使用JTATransactionManager的話,則需要訪問(wèn)TransactionManager |
propagation.NOT_SUPPORTED | 表示該方法不應(yīng)該運(yùn)行在事務(wù)中 。如果存在當(dāng)前事務(wù),在該方法運(yùn)行期間,當(dāng)前事務(wù)將被掛起。如果使用JTATransactionManager的話,則需要訪問(wèn)TransactionManager |
propagation.NEVER | 表示當(dāng)前方法不應(yīng)該運(yùn)行在事務(wù)上下文中 。如果當(dāng)前正有一個(gè)事務(wù)在運(yùn)行,則會(huì)拋出異常 |
propagation.NESTED | 表示如果當(dāng)前已經(jīng)存在一個(gè)事務(wù),那么該方法將會(huì)在嵌套事務(wù)中運(yùn)行 。嵌套的事務(wù)可以獨(dú)立于當(dāng)前事務(wù)進(jìn)行單獨(dú)地提交或回滾。如果當(dāng)前事務(wù)不存在,那么其行為與propagation.REQUIRED一樣。注意各廠商對(duì)這種傳播行為的支持是有所差異的。可以參考資源管理器的文檔來(lái)確認(rèn)它們是否支持嵌套事務(wù) |
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
基于visualvm監(jiān)控類實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了基于visualvm監(jiān)控類實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Java?MethodHandles介紹與反射對(duì)比區(qū)別詳解
這篇文章主要為大家介紹了Java?MethodHandles介紹與反射對(duì)比區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11詳解簡(jiǎn)單基于spring的redis配置(單機(jī)和集群模式)
這篇文章主要介紹了詳解簡(jiǎn)單基于spring的redis配置(單機(jī)和集群模式),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02利用Java獲取文件名、類名、方法名和行號(hào)的方法小結(jié)
這篇文章運(yùn)用實(shí)例代碼給大家介紹了利用Java怎樣獲取文件名、類名、方法名和行號(hào),有需要的可以參考借鑒,下面一起來(lái)看看吧。2016-08-08非常適合新手學(xué)生的Java線程池超詳細(xì)分析
作者是一個(gè)來(lái)自河源的大三在校生,以下筆記都是作者自學(xué)之路的一些淺薄經(jīng)驗(yàn),如有錯(cuò)誤請(qǐng)指正,將來(lái)會(huì)不斷的完善筆記,幫助更多的Java愛(ài)好者入門2022-03-03如何通過(guò)Maven倉(cāng)庫(kù)安裝Spire系列的Java產(chǎn)品
這篇文章主要介紹了如何通過(guò)Maven倉(cāng)庫(kù)安裝Spire系列的Java產(chǎn)品,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07java獲取指定開(kāi)始時(shí)間與結(jié)束時(shí)間之間的所有日期
這篇文章主要為大家詳細(xì)介紹了java獲取指定開(kāi)始時(shí)間與結(jié)束時(shí)間之間的所有日期,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05JDBC數(shù)據(jù)庫(kù)連接過(guò)程及驅(qū)動(dòng)加載與設(shè)計(jì)模式詳解
這篇文章主要介紹了JDBC數(shù)據(jù)庫(kù)連接過(guò)程及驅(qū)動(dòng)加載與設(shè)計(jì)模式詳解,需要的朋友可以參考下2016-10-10IDEA Maven源修改為國(guó)內(nèi)阿里云鏡像的正確方式
為了加快 Maven 依賴的下載速度,可以將 Maven 的中央倉(cāng)庫(kù)源修改為國(guó)內(nèi)的鏡像,比如阿里云鏡像,以下是如何在 IntelliJ IDEA 中將 Maven 源修改為阿里云鏡像的詳細(xì)步驟,感興趣的同學(xué)可以參考閱讀一下2024-09-09