最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

幾個直擊心靈的spring拷問

2020-12-22 10:34 作者:每天一個禿頂小技巧  | 我要投稿

今天這一篇主要想圍繞著Spring的循環(huán)依賴問題以及終極靈魂拷問如何手寫Spring的問題講講。
一、Spring循環(huán)依賴

1.什么是循環(huán)依賴


Spring中的循環(huán)依賴一直是Spring中一個很重要的話題,一方面是因?yàn)樵创a中為了解決循環(huán)依賴做了很多處理,另外一方面是因?yàn)槊嬖嚨臅r候,如果問到Spring中比較高階的問題,那么循環(huán)依賴必定逃不掉。所以還是可以看一下這塊的源碼,看看Spring是如何解決循環(huán)依賴的問題的。


Spring中之所以會出現(xiàn)循環(huán)依賴跟Bean的生命周期有關(guān)系,在創(chuàng)建一個Bean的過程中如果依賴的另外一個Bean還沒有創(chuàng)建,就會需要去創(chuàng)建依賴的那個Bean,而如果兩個Bean相互依賴的話,就會出現(xiàn)循環(huán)依賴的問題。體現(xiàn)到代碼層次就是像下面這個樣子的,比如兩個對象相互依賴:

2.三級緩存方案


假設(shè)按照上面代碼Class A 和 B,按照從A->B的順序來實(shí)例化,Spring創(chuàng)建bean的過程可以分為三個階段:
1、實(shí)例化,對應(yīng)方法:AbstractAutowireCapableBeanFactory # createBeanInstance方法
2、屬性注入,對應(yīng)方法:AbstractAutowireCapableBeanFactory # populateBean方法
3、初始化,對應(yīng)方法:AbstractAutowireCapableBeanFactory # initializeBean
所以執(zhí)行順序是先在這個類中的 AbstractBeanFactory 按調(diào)用鏈執(zhí)行如下三個方法:


getSingleton(beanName, true)
這是個重點(diǎn)方法,該方法實(shí)際上就是到緩存中嘗試去獲取Bean,整個緩存分為三級
singletonObjects,一級緩存,存儲的是所有創(chuàng)建好了的單例Bean
earlySingletonObjects,完成實(shí)例化,但是還未進(jìn)行屬性注入及初始化的對象
singletonFactories,提前暴露的一個單例工廠,二級緩存中存儲的就是從這個工廠中獲取到的對象


因?yàn)槭堑谝淮蝿?chuàng)建,因此上面的三級緩存都未命中,此時會進(jìn)入getSingleton的另外一個重載方法getSingleton(beanName, singletonFactory)。

這里我們知道 singletonFactory 是需要等待createBean(beanName, mbd, args) 方法的返回,然后作為第二個輸入?yún)?shù)給到下面 getSingleton 方法。

上面的代碼主要實(shí)現(xiàn)了:將已經(jīng)完全創(chuàng)建好了的單例Bean放入一級緩存中。在前面一步 createBean()方法的創(chuàng)建實(shí)例過程中還有一個 doCreateBean 方法,里面還有這樣一段代碼:

非AOP的二級緩存

這個地方的BeanPostProcessor后置處理器,只在處理AOP的實(shí)例對象時才會發(fā)揮作用,如果不考慮AOP,代碼就是:

可見,對于非Aop實(shí)例對象,這個工廠直接將實(shí)例化階段創(chuàng)建的對象返回了!


現(xiàn)在整體來梳理一下,繼續(xù)走A對象創(chuàng)建的流程,通過this.singletonFactories.put(beanName, singletonFactory)

這個方法只是添加了一個工廠,通過這個工廠(ObjectFactory)的getObject方法可以得到一個對象。當(dāng)A完成了實(shí)例化并添加進(jìn)了三級緩存后,就要開始為A進(jìn)行屬性注入了,在注入時發(fā)現(xiàn)A依賴了B,那么這個時候Spring又會去getBean(b),然后反射調(diào)用setter方法完成屬性注入。因?yàn)锽需要注入A,所以在創(chuàng)建B的時候,又會去調(diào)用getBean(a),這個時候就又回到之前的流程了,但是不同的是,之前的getBean是為了創(chuàng)建Bean,而此時再調(diào)用getBean不是為了創(chuàng)建了,而是要從緩存中獲取,因?yàn)橹癆在實(shí)例化后已經(jīng)將其放入了三級緩存singletonFactories中,此時getBean(a)的二級緩存會通過調(diào)用三級緩存的facotry,通過工廠的getObject方法將對象放入到二級緩存中并返回,所以此時getBean(a)的流程就是這樣子了,一個清晰的流程圖如下:

結(jié)合了AOP的循環(huán)依賴

如果在開啟AOP的情況下,那么就是調(diào)用 getEarlyBeanReference 方法對應(yīng)的源碼如下:

對A進(jìn)行了AOP代理的話,那么此時getEarlyBeanReference將返回一個代理后的對象,而不是實(shí)例化階段創(chuàng)建的對象,這樣就意味著B中注入的A將是一個代理對象而不是A的實(shí)例化階段創(chuàng)建后的對象。整個注入的流程圖就變成了如下:


3.循環(huán)依賴的總結(jié)


1、Spring到底是如何解決循環(huán)依賴的呢,這里來一波文字的總結(jié):
Spring通過三級緩存解決了循環(huán)依賴,其中一級緩存為單例池(singletonObjects),二級緩存為早期曝光對象earlySingletonObjects,三級緩存為早期曝光對象工廠(singletonFactories)。當(dāng)A、B兩個類發(fā)生循環(huán)引用時,在A完成實(shí)例化后,就使用實(shí)例化后的對象去創(chuàng)建一個對象工廠,并添加到三級緩存中,如果A被AOP代理,那么通過這個工廠獲取到的就是A代理后的對象,如果A沒有被AOP代理,那么這個工廠獲取到的就是A實(shí)例化的對象。當(dāng)A進(jìn)行屬性注入時,會去創(chuàng)建B,同時B又依賴了A,所以創(chuàng)建B的同時又會去調(diào)用getBean(a)來獲取需要的依賴,此時的getBean(a)會從緩存中獲取,第一步,先獲取到三級緩存中的工廠;第二步,調(diào)用對象工工廠的getObject方法來獲取到對應(yīng)的對象,得到這個對象后將其注入到B中。

緊接著B會走完它的生命周期流程,包括初始化、后置處理器等。當(dāng)B創(chuàng)建完后,會將B再注入到A中,此時A再完成它的整個生命周期。至此,循環(huán)依賴結(jié)束!
2、為啥要用三級緩存,是否可以用二級緩存


在普通的循環(huán)依賴的情況下,三級緩存沒有任何作用。三級緩存實(shí)際上跟Spring中的AOP相關(guān)。AOP場景下的getEarlyBeanReference 會拿到一個代理的對象,但是不確定有沒有依賴,需不需要用到這個依賴對象,所以先給一個工廠放到三級緩存里。
這個工廠的目的在于延遲對實(shí)例化階段生成的對象的代理,只有真正發(fā)生循環(huán)依賴的時候,才去提前生成代理對象,否則只會創(chuàng)建一個工廠并將其放入到三級緩存中,但是不會去通過這個工廠去真正創(chuàng)建對象。


二、如何手寫一個Spring框架


1、一個手寫IoC容器的思路


IOC的實(shí)現(xiàn)思路如下:

  • 首先有一個配置文件定義了應(yīng)用的基礎(chǔ)包, 也就是Java源碼路徑.

  • 讀取基礎(chǔ)包名, 然后通過類加載器獲取到應(yīng)用中所有的Class對象, 存儲到一個集合中.

  • 獲取應(yīng)用中所有Bean (Controller和Service) 的Class對象, 通過反射創(chuàng)建實(shí)例, 然后存儲到 Bean容器中.

  • 遍歷Bean容器中的所有Bean, 為所有帶 @Autowired 注解的屬性注入實(shí)例.

  • IOC操作要在應(yīng)用啟動時就完成, 所以必須寫在靜態(tài)代碼塊中.

仿寫spring容器



2、一個手寫SpringMVC的思路


(1)讀取配置


SpringMVC本質(zhì)上是一個Servlet,這個 Servlet 繼承自 HttpServlet。FrameworkServlet負(fù)責(zé)初始化SpringMVC的容器,并將Spring容器設(shè)置為父容器。因?yàn)楸疚闹皇菍?shí)現(xiàn)SpringMVC,對于Spring容器不做過多講解。
為了讀取web.xml中的配置,我們用到ServletConfig這個類,它代表當(dāng)前Servlet在web.xml中的配置信息。通過web.xml中加載我們自己寫的MyDispatcherServlet和讀取配置文件。


(2)初始化階段


在前面我們提到DispatcherServlet的initStrategies方法會初始化9大組件,但是這里將實(shí)現(xiàn)一些SpringMVC的最基本的組件而不是全部,按順序包括:

  • 加載配置文件

  • 掃描用戶配置包下面所有的類

  • 拿到掃描到的類,通過反射機(jī)制,實(shí)例化。并且放到ioc容器中(Map的鍵值對 beanName-bean) beanName默認(rèn)是首字母小寫

  • 初始化HandlerMapping,這里其實(shí)就是把url和method對應(yīng)起來放在一個k-v的Map中,在運(yùn)行階段取出

(3)運(yùn)行階段


每一次請求將會調(diào)用doGet或doPost方法,所以統(tǒng)一運(yùn)行階段都放在doDispatch方法里處理,它會根據(jù)url請求去HandlerMapping中匹配到對應(yīng)的Method,然后利用反射機(jī)制調(diào)用Controller中的url對應(yīng)的方法,并得到結(jié)果返回。按順序包括以下功能:

  • 異常的攔截

  • 獲取請求傳入的參數(shù)并處理參數(shù)

  • 通過初始化好的handlerMapping中拿出url對應(yīng)的方法名,反射調(diào)用

仿寫springmvc容器


3、一個手寫SpringMVC的思路
1 掃描 aop 包, 獲取 aspect 的類
2 根據(jù) 切點(diǎn) 獲取該切點(diǎn)的 類 和 方法
3 根據(jù)配置的 類 和 方法 為該類生成一個代理對象
4 將改代理對象放入 bean Map 中
5 調(diào)用的時候 將代理對象 轉(zhuǎn)換成需要的對象


作者:千淘萬漉
鏈接:jianshu.com/p/e68df1bfb
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

想要了解更多可以點(diǎn)擊:

尚學(xué)堂最新2020版Java300集教程課程

幾個直擊心靈的spring拷問的評論 (共 條)

分享到微博請遵守國家法律
彭阳县| 曲松县| 曲水县| 东乌珠穆沁旗| 大丰市| 政和县| 枞阳县| 紫金县| 娱乐| 文成县| 拜城县| 龙南县| 丹阳市| 高密市| 濉溪县| 临城县| 黎川县| 榆社县| 三原县| 宜昌市| 铁岭市| 禄丰县| 周口市| 云梦县| 博兴县| 克山县| 波密县| 舟山市| 顺昌县| 昌邑市| 启东市| 康定县| 天镇县| 仙居县| 临洮县| 甘洛县| 吉安县| 永嘉县| 彭水| 泉州市| 浙江省|