java大廠面試題整理(十二)Spring循環(huán)依賴

Spring是java的重要框架俱饿。這塊的知識點也很多糟港,這里只是簡單的挑了比較高頻的兩個問題來講:一個是spring的aop的順序算是開胃菜琉兜,重點就是spring 的循環(huán)依賴凯正。下面開始正文:

Spring 的aop順序。

aop簡單來講就是面向切面編程豌蟋。spring利用aop可以對業(yè)務(wù)邏輯的各個部分進(jìn)行隔離廊散,從而使業(yè)務(wù)邏輯的各個部分之間耦合度降低。提高程序的可用性和開發(fā)效率梧疲。
這么說是很官方的語言允睹,舉一個實際工作中的例子:一般運營后臺的一些增刪改操作运准,我們常常會把操作人,操作參數(shù)缭受,ip等記錄成日志胁澳。這樣的話某一天某個管理員失心瘋故意損壞數(shù)據(jù)我們也可以通過這個日志定位到某個人,某個ip米者,從而來判斷是盜號了還是怎么樣的韭畸。
而這樣的記錄日志操作是每一個方法都要調(diào)用的。一般這個時候雖然我們也可以每個方法中調(diào)用同樣的代碼蔓搞。但是更好一點的方式是直接將記錄操作用aop的前置或者后置通知來做記錄日志的操作胰丁。下面簡單介紹下aop中常用注解:

  • @Before:前置通知:目標(biāo)方法執(zhí)行之前執(zhí)行。
  • @After:后置通知:目標(biāo)方法之后執(zhí)行(始終執(zhí)行)
  • @AfterReturning:返回后通知:執(zhí)行方法結(jié)束前執(zhí)行(異常不執(zhí)行)
  • @AfterThrowing:異常通知:出現(xiàn)異常時執(zhí)行
  • @Around:環(huán)繞通知:環(huán)繞目標(biāo)方法執(zhí)行

以上的注解其實很容易理解喂分,甚至沒注解的話只看單詞都能猜的差不多锦庸。但是其實這些注解的執(zhí)行順序是很有意思的。比如@After和@AfterRentruning/@AfterThrowing哪個先執(zhí)行蒲祈?這些執(zhí)行順序其實不是一成不變的甘萧。
我們現(xiàn)在常用的是Spring boot框架。但是18梆掸,19年比較流行的是Spring boot1扬卷。近兩年用的都是Spring boot2.而sb1的底層是spring4.sb2的底層是spring5.
spring4和spring5中,這些注解的執(zhí)行順序是不同的沥潭。下面讓我們簡單的用代碼看一下順序邀泉。先用spring5嬉挡,也就是spring boot2版本測試下钝鸽,如下代碼:


spring boot版本2以上

隨便寫個方法:


劃重點,返回的是包裝類

然后寫環(huán)繞方法:
@Aspect
@Component
public class MyAspect {
    
    @Before("execution(public Integer lsj.service.impl.AspectImpl.*(..))")
    public void before() {
        System.out.println("<<<<<<<<<<<<<<<<<before-前置通知");
    }
    
    @After("execution(public Integer lsj.service.impl.AspectImpl.*(..))")
    public void after() {
        System.out.println("<<<<<<<<<<<<<<<<after-后置通知");
    }
    
    @AfterReturning("execution(public Integer lsj.service.impl.AspectImpl.*(..))")
    public void aft() {
        System.out.println("<<<<<<<<<<<<<<AfterReturning-返回通知");
    }
    
    @AfterThrowing("execution(public Integer lsj.service.impl.AspectImpl.*(..))")
    public void at() {
        System.out.println("<<<<<<<<<<<<<<AfterThrowing-異常通知");
    }
    
    @Around("execution(public Integer lsj.service.impl.AspectImpl.*(..))")
    public void around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("<<<<<<<<<<<<<<<Around-環(huán)繞通知前");
        point.proceed();
        System.out.println("<<<<<<<<<<<<<<<Around-環(huán)繞通知后");
    }
}

這個也沒什么業(yè)務(wù)邏輯庞钢,就是單純的打印語句以便于看清執(zhí)行順序拔恰。重點是我們的方法不要返回數(shù)值類型,要返回包裝類基括,不然會報個錯颜懊。

訪問這個方法

注意這里先用正常的數(shù)字訪問,然后y=0訪問(y=0會除以0報錯)
正常訪問執(zhí)行順序

異常執(zhí)行順序

同樣的代碼风皿,我只改一下spring boot的版本:
![修改spring boot為1.x版本](https://upload-images.jianshu.io/upload_images/16553345-70252c1784f4bc74.png?im 一張對比下:
spring4和spring5通知順序變化

明顯能看出來河爹,這里主要的@After和@Around還有另外兩個異常或者正常返回的順序桐款。明顯在4的時候其實邏輯是不合適的咸这、因為@After是無論如何都是必須執(zhí)行的。既然這樣應(yīng)該是finally里的東西魔眨,也就是最后執(zhí)行媳维。所以在spring5改成了正衬鹧或者異常返回之后執(zhí)行。而環(huán)繞的話有點類似于方法的第一行和最后一行的一個輸出語句侄刽。代碼走到了會執(zhí)行指黎。但是代碼走不到就執(zhí)行不了。下面一張簡單的五種注解的位置:
五種注解執(zhí)行位置如圖

這個我們可以理解為輸出語句就是切面的方法州丹。至于這個方法能不能執(zhí)行還得看代碼能不能走到這個方法中醋安。不要死記硬背執(zhí)行順序,而是要知道是在什么情況下才能執(zhí)行到墓毒。
這個比較簡單茬故,就不多說了,下面說說spring的循環(huán)依賴蚁鳖。

Spring中的循環(huán)依賴

跟這個問題相關(guān)的問題有一下:

  • spring中的三級緩存是什么磺芭?三個Map有什么異同?
  • 一般我們說的spring容器是什么醉箕?
  • 如何檢測是否存在循環(huán)依賴钾腺?實際開發(fā)中見過循環(huán)依賴的異常么?
  • 多例的情況下循環(huán)依賴問題為什么無法解決讥裤?

下面我們一點一點的介紹和學(xué)習(xí)放棒。
什么是循環(huán)依賴呢?
簡單來說就似乎多個bean之間相互依賴己英。形成了一個閉環(huán)间螟。比如A依賴于B,B依賴于C损肛,C依賴于A厢破。下面用最簡單的代碼表示下這種情況:

循環(huán)依賴

而通常來講,面試問Spring容器內(nèi)部如何解決循環(huán)依賴治拿,一定是指默認(rèn)的單例Bean中屬性互相引用的場景摩泪。如下代碼示例:
spring容器中的循環(huán)依賴

注意只要形成閉環(huán)就算是循環(huán)依賴,不管是2個劫谅,還是多個bean见坑。反正到這里我們起碼簡單的明白了什么是循環(huán)依賴。
兩種注入方式對循環(huán)依賴的影響
循環(huán)依賴的提出和解決其實官網(wǎng)上也都有說捏检,下面的官網(wǎng)截圖:
spring官網(wǎng)對循環(huán)依賴的建議

spring官網(wǎng)說明中文版

其實這里也涉及到了兩種spring的注入方式:構(gòu)造器注入和setter注入荞驴。
而官網(wǎng)很明確的說了,構(gòu)造器注入不推薦贯城,容易產(chǎn)生循環(huán)依賴的問題熊楼。并且如果檢測到代碼中存在這種現(xiàn)象會報異常。
而我們AB循環(huán)依賴的問題只要A的注入方式是setter并且是singleton冤狡,就不會有循環(huán)依賴的問題(spring容器中默認(rèn)都是單例的孙蒙。而且B這樣也可以解決)项棠。
下面代碼演示下兩種注入方式的循環(huán)依賴:
構(gòu)造器注入

如上A,B兩個類。別說Spring了挎峦,我們自己都創(chuàng)建不出來A或者B對象了香追。因為創(chuàng)建A要先有B,創(chuàng)建B先有A坦胶。有點類似于先有雞先有蛋的哲學(xué)問題了透典。所以說這個構(gòu)造器的循環(huán)依賴是解決不了的。而setter方式注入的話顿苇,是可以解決的峭咒。
setter方式的循環(huán)依賴

如果代碼是這樣無論A還是B都是很容易創(chuàng)建的了。而且其實我們在實際代碼中也經(jīng)常這樣做纪岁。比如說訂單商品表和訂單表凑队。有時為了特定的需求會互相注入的。然后因為是用setter方式注入幔翰,所以兩個對象都有默認(rèn)的無參構(gòu)造漩氨,所以很容易就可以創(chuàng)建對象。
其實上面說的都是純java代碼的循環(huán)依賴遗增。下面開始說spring容器的循環(huán)依賴(其實原理類似叫惊。畢竟spring也是對java代碼的封裝)。
Spring中的循環(huán)依賴
Spring容器中默認(rèn)的單例(singleton)的場景是支持循環(huán)依賴的做修。而原型(Prototype)的場景是不持支循環(huán)依賴的霍狰。
之所以這樣的原因就似乎因為:Spring內(nèi)部通過三級緩存來解決循環(huán)依賴的。
Spring中的三級緩存
說到spring的三級緩存饰及,一個很重要的類要被提到了:DefaultSingletonBeanRegistry
我們可以直接看下這個類的源碼:
DefaultSingletonBeanRegistry的三個map

這三個map中:

  • singletonObjects:一級緩存(也叫單例池)蔗坯。存放已經(jīng)經(jīng)歷了完整生命周期的bean對象。
  • earlySingtonObjects:二級緩存旋炒。存放早期暴露出來的bean對象步悠,bean的生命周期未結(jié)束(屬性還沒填充完)
  • singletonFactories:三級緩存签杈。存放可以生成Bean的工廠瘫镇。

只有單例的bean會通過三級緩存提前暴露來解決循環(huán)依賴的問題。而非單例的bean每次從容器中獲取的都是一個新的對象答姥。都會重新創(chuàng)建铣除,所以非單例的bean是沒有緩存的,不會將其放到三級緩存中鹦付。

debug調(diào)試查看spring三級緩存工作運作

想要明白運作過程有一些前備知識簡單的說一下:
實例化/初始化:

  • 實體化:內(nèi)存中申請了一塊內(nèi)存空間尚粘。(可以理解為想要蓋房子。跟國家申請了一塊地)
  • 初始化屬性填充:完成屬性的各種賦值(開始在這塊地上動工敲长。打地基蓋房子等郎嫁。)

三級緩存:

  • singletonObjects:一級緩存(也叫單例池)秉继。存放已經(jīng)經(jīng)歷了完整生命周期的bean對象。
  • earlySingtonObjects:二級緩存泽铛。存放早期暴露出來的bean對象尚辑,bean的生命周期未結(jié)束(屬性還沒填充完)已經(jīng)實例化但沒有初始化。
  • singletonFactories:三級緩存盔腔。存放可以生成Bean的工廠杠茬。

三級緩存的使用流程:

  • A創(chuàng)建過程中需要B,于是A將自己放到三級緩存里面弛随,去實例化B瓢喉。
  • B實例化的時候發(fā)現(xiàn)需要A,于是B先查一級緩存舀透,發(fā)現(xiàn)一級緩存中沒有A栓票。于是查二級緩存,發(fā)現(xiàn)還是沒有愕够,最后查三級緩存逗载,發(fā)現(xiàn)了A,然后把三級緩存里面的A放到了二級緩存中链烈,并且刪除了三級緩存中的A厉斟。
  • B順利初始化完成,將自己放到一級緩存中(此時B里面的A依然是實例化狀態(tài))强衡。然后回來接著創(chuàng)建A擦秽,此時B已經(jīng)創(chuàng)建結(jié)束,直接從一級緩存中拿到B漩勤,然后完成創(chuàng)建感挥。并且A將自己存到一級緩存中。

四大方法:

  • getSingleton:從容器中獲取這個單例bean(這個有多個重載方法越败。其中有不同的業(yè)務(wù)邏輯触幼,主要是用來把bean在不同級別的緩存中移動。)
  • doCreateBean:這個其實包含了populateBean究飞,可以理解為初始化和實例化bean的方法置谦。但是從這里出來的bean不在一級緩存中,外層套了個getSingleton方法將bean放入到一級緩存亿傅。
  • populateBean: 填充實例化的bean的屬性媒峡。如果有屬性沒有那么會遞歸創(chuàng)建屬性的bean的方法套娃。
  • addSingleton:將這個初始化的bean放入到一級緩存中葵擎。

說了這么多前置知識谅阿,下面讓我們跟著debug走一遍代碼。親眼看看執(zhí)行過程:
代碼還是之前的注入A,B兩個組件的代碼。簡單貼出來:

準(zhǔn)備代碼

如上代碼签餐。然后我們跟著代碼一步一步走:
注意最開始是幾個run方法一層一層走我就不說了寓涨,直接到正經(jīng)的方法中:
最后一個run不是套娃,是代碼實現(xiàn)

注意我紅色框起來的方法氯檐,這個方法執(zhí)行完會在公平打印出A,B創(chuàng)建完成缅茉。所以真正的創(chuàng)建過程在這個方法里。另外說一個細(xì)節(jié):在prepareContext方法執(zhí)行完后會打印spring boot的logo男摧。
其實我們完全可以第一個斷點就下在這個SpringApplication類的315行(就是refreshContext方法這行蔬墩。前面的都是跳來跳去的沒啥意義)
然后走進(jìn)這個方法:
順著方法往下走到了這行

還是這個類,走到了758行耗拓。同樣走完這行會A,B創(chuàng)建完拇颅。所以創(chuàng)建過程在這個方法里,進(jìn)去看:
注意換類了

然后這個方法一步一步走乔询,我是先無腦過樟插。然后注意看控制臺。確定這個創(chuàng)建過程發(fā)現(xiàn)的方法竿刁,一點點細(xì)化黄锤。這樣做就是要不斷debug。耐心點就好了食拜。繼續(xù)往下走:
框起來的方法創(chuàng)建了A,B

然后發(fā)現(xiàn)finishBeanFactoryInitialization(beanFactory);這行代碼執(zhí)行的過程中創(chuàng)建了A,B兩個對象鸵熟。所以咱們可以把斷點設(shè)置在這行代碼中并且重新debug:
更加細(xì)致的確認(rèn)了創(chuàng)建的方法

我框起來的方法是創(chuàng)建A,B的方法负甸。繼續(xù)往里走:
getBean方法

注意這里定位到了getBean方法流强。而且這個方法是創(chuàng)建所有的bean,容器中有好多bean呻待,所以這里要多跑幾遍打月。注意看當(dāng)前遍歷到是是不是我們測試用的這兩個對象。繼續(xù)往下走:
getBean方法繼續(xù)往下

doGetBean方法是四大方法之一蚕捉。進(jìn)入到這個方法后發(fā)現(xiàn)有個從容器中獲取bean的方法:
getSingleton

同樣這個方法也是四大方法之一奏篙。雖然我們知道這個時候肯定是容器中沒有這個bean了,但是我們依然可以進(jìn)入看看方法:
三個map挨個查找

然后方法一直往下走迫淹,
createBean方法

注意這里會走進(jìn)這個getSingleton方法中秘通,返回中調(diào)用了createBean這個方法。并且在這個方法中輸出了A創(chuàng)建千绪。我們進(jìn)入這個方法打個斷點伶氢。
準(zhǔn)備創(chuàng)建a

這個代碼的調(diào)試就是順著一步一步往下走橙数,其實一來可以向我那樣無腦過,看控制臺輸出打印語句記住這行代碼下次調(diào)試進(jìn)入到方法里拱她,也可以每一個方法都點進(jìn)去瞅一眼,反正這個方法中很明顯應(yīng)該進(jìn)入的方法就是我框起來的這個doCreateBean瑞妇。
doCreateBean

進(jìn)入到這個方法中前幾行的邏輯也挺簡單的稿静。如果這個bean構(gòu)造器是null。那么執(zhí)行createBeanInstance方法辕狰。注意這個時候我們確定還沒有創(chuàng)建a這個bean改备,所以可以考慮格外注意create的方法。
createBeanInstance方法

繼續(xù)走進(jìn)createBeanInstance方法:
createBeanInstance方法

首先這個方法是通過反射做了很多的事蔓倍。
代碼執(zhí)行完這行發(fā)現(xiàn)A已經(jīng)創(chuàng)建

emmmm...再一次跟丟了悬钳,不過這個時候a中的b是null:
a中的b是null

因為上面的A只是單純的創(chuàng)建了,B也沒填充偶翅,所以我去翻了一下createBeanInstance的方法,從注釋可以看出來最后調(diào)用無參構(gòu)造器創(chuàng)建了對象:
image.png

所以這一步就不重新走了默勾,我們繼續(xù)往下:
doCreateBean方法

繼續(xù)走這個方法,注意看這個判斷比較有意思聚谁,還記得當(dāng)時三級緩存中三個map中的二級緩存的名字就是earlySingletonObjects吧母剥。然后這個判斷中有個變量,spring中默認(rèn)值就是true:
開啟三級緩存

所以這里的判斷其實本質(zhì)上就是判斷當(dāng)前bean是不是單例的形导。是的話就支持三級緩存环疼,也就是進(jìn)入到這個if方法中,如下方法:
addSingletonFactory

這里是一個lambda朵耕,我是先進(jìn)入到方法中打個斷點然后往下debug的:
addSingletonFactory方法

這個方法的邏輯很明了:如果一級緩存中沒有這個bean炫隶,那么把這個bean放入到三級緩存中,并且從二級緩存中移除這個bean(其實事實上這個時候二級緩存中是沒有a的阎曹,這個刪除就是一個空刪)等限。最后一個不是三級緩存中的map,所以先不管芬膝。繼續(xù)往下走望门。
注意現(xiàn)在這個時候:a這個bean已經(jīng)在三級緩存中了。
繼續(xù)往下走代碼又走到了四大方法之一:populateBean锰霜。也就是屬性填充筹误,注意這個時候我們要把b填充進(jìn)來了:
populateBean方法

判斷b屬性容器中有沒有

這塊的方法名字比較明顯,正常的情況下是沒有的癣缅,所以這個走了下面這個分支:
applyPropertyValues方法

往這個方法里面走:
applyPropertyValues中有這么一行代碼

這個方法是需要b這個屬性但是b又沒有厨剪,所以解決這個value。我們繼續(xù)往里走:
這個方法的走向是進(jìn)了這個分支

然后繼續(xù)往resolveReference方法里走:
注意走到了getbean方法

這個方法代碼走到了我紅框框起來的方法友存,getBean祷膳。這個名字很眼熟吧,我們走進(jìn)去:
兜兜轉(zhuǎn)轉(zhuǎn)屡立,回到原點

到了這我們必須眼熟啊直晨,之前創(chuàng)建a的時候從這里開始的。所以說現(xiàn)在需要b,所以b也要和a一樣走一遍勇皇。這我就不一步步調(diào)試走了罩句,因為A,B是我們自己寫的敛摘,配置啥的都一樣门烂,我們盲猜就能猜到走和a一樣的流程。
直接用文字來講:

  1. getBean方法進(jìn)入流程
    2.doGetBean方法開始去獲取bean
  2. getSingleton方法試圖直接從容器中拿bean(這個步驟沒啥好說的兄淫,但是第一次進(jìn)來肯定獲取不到屯远,所以走下面的步驟)
  3. 如果沒有則再走getSingleton方法(注意這里和第三步的getSingleton是兩個方法。上一步單純的三級緩存中拿捕虽。而這個方法中會有業(yè)務(wù)邏輯)
  4. getSingleton方法中有個getObject方法是用匿名內(nèi)部類的方式書寫的(第四步中寫的)慨丐。是createBean方法。
  5. createBean方法中經(jīng)過一系列判斷薯鳍,進(jìn)入doCreateBean方法
  6. doCreateBean方法中調(diào)用createBeanInstance利用反射真正的創(chuàng)建了B這個bean(但是這個時候是無屬性填充的)
  7. 創(chuàng)建完后調(diào)用addSingletonFactory試圖把這個bean添加到工廠(如果一級緩存中沒有這個bean咖气,則把這個bean從二級緩存中刪除,放到三級緩存挖滤,但是其實正常來講二級緩存中也沒這個bean崩溪,所以多一步刪除為了保險吧)
  8. 當(dāng)前b這個bean已經(jīng)完成初始化,接下來進(jìn)入populateBean方法斩松,進(jìn)行屬性填充
  9. 我們發(fā)現(xiàn)b中有個屬性a伶唯,因為a我們之前已經(jīng)實例化了,所以容器中有a惧盹,所以調(diào)用applyPropertyValues方法填充這個屬性乳幸。
  10. applyPropertyValues方法中對所有屬性進(jìn)行resolveValueIfNecessary方法判斷,a進(jìn)入此方法
  11. resolveValueIfNecessary方法返回值是resolveReference方法的結(jié)果
  12. resolveReference方法會走進(jìn)getBean方法試圖從容器中拿a
  13. 循環(huán)走到getBean方法的doGetBean方法钧椰,進(jìn)入到getSingleton方法獲取bean
  14. 這個方法是第一個getSingleton方法粹断,從三級緩存中依次獲取bean的。而且注意這個時候a已經(jīng)在三級緩存中了嫡霞,所以進(jìn)入最后一個邏輯中瓶埋,從三級緩存中獲取bean刪除,并且放入到二級緩存中诊沪。而且這個doGetBean方法中會直接將a返回养筒。
  15. 將a填充到b的這個屬性中。
  16. 注意這里是重點端姚。上面的邏輯中從第5步開始這些代碼都發(fā)生在一個匿名內(nèi)部類中晕粪。而這個代碼的外層是 getSingleton方法。所以現(xiàn)在回到這個方法中渐裸。
  17. 這個getSingleton方法的最后一步是addSingleton方法巫湘。(getSingleton方法有多個重載装悲,這里不要記混了)
  18. 這個方法將此bean也就是b放入一級緩存中,并且刪除二三級緩存的此bean剩膘。
  19. 至此b實例化完成并且初始化完成衅斩。
  20. 我們創(chuàng)建b是在a需要b的時候走進(jìn)來的盆顾。當(dāng)b創(chuàng)建完成繼續(xù)走a的填充屬性的代碼怠褐。把b填充給a。a也初始化完成您宪。


    addSingleton方法將bean放入一級緩存并且刪除二三級緩存中的bean

    getSingleton方法1

    doGetBean方法中用getSingleton方法傳匿名內(nèi)部類方式用createBean創(chuàng)建bean

而三級緩存能解決循環(huán)依賴的主要原因:bean的創(chuàng)建是分為創(chuàng)建原始bean對象和填充對象屬性和初始化兩個步驟的奈懒。也就是說Spring解決循環(huán)依賴依靠的是bean的“中間態(tài)”這個概念。中間態(tài)是指已經(jīng)實例化但是還沒初始化的狀態(tài)宪巨。
三級緩存的學(xué)名:

  • 一級緩存:單例池磷杏。
  • 二級緩存:提前曝光對象
  • 三級緩存: 提前曝光對象工廠

所以循環(huán)依賴中我們可以先解決有沒有的問題。然后再去一點點填充屬性捏卓。而之所以必須單例才能實現(xiàn)循環(huán)依賴也是這個原因极祸。我感覺說到這里循環(huán)依賴就說的挺明白了。

一點不夸張的說這段debug我用了三四個小時才算是理明白這個代碼的走向怠晴,流程什么的遥金。而且就像一開始說的一不小心就debug丟了,一切從頭再來蒜田。但是這個是真的很有意思的一個東西稿械,有時候調(diào)著調(diào)著就會覺得寫出這種代碼的人簡直神仙。首先閱讀spring源碼可能除了吹NB不會有什么立刻馬上的明顯的提高冲粤。畢竟工作中寫個循環(huán)依賴啥的太扯了美莫。但是閱讀本身是一個開拓思路的好途徑。會有一種恍然大悟的感覺梯捕,啊厢呵,原來代碼還能這么寫。另外也是一個學(xué)習(xí)方式傀顾,畢竟一般跳槽大多數(shù)都會接手現(xiàn)有項目襟铭,所以學(xué)會閱讀源碼,快速理解源碼都挺有用的锣笨。甚至換個角度:看別人寫的好的小說是放松蝌矛,看別人寫的好的代碼不也是享受和放松么?這是一種很有成就感也很有意義的事错英。學(xué)會享受閱讀源碼入撒,尤其是比較復(fù)雜的源碼真的不錯。
另外我一直強調(diào)的一個觀點:別難為自己椭岩。如果在代碼中找不到樂趣茅逮,看代碼如上墳璃赡,就換個工作吧。這個社會搬磚也能養(yǎng)活自己献雅,還不用動腦碉考。干嘛非要去做讓自己抑郁的事呢?如果只是為了混日子也沒必要學(xué)這些挺身,還是那句話侯谁,放過自己。
本篇筆記就記到這里章钾,如果稍微幫到你了記得點個喜歡點個關(guān)注墙贱。也祝大家工作順順利利,生活健健康康~贱傀!另外因為我也不知道我圖文表述的夠不夠清楚惨撇,如果有同樣在閱讀spring源碼的小伙伴可以留言或者私聊我加個好友一起討論呀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末府寒,一起剝皮案震驚了整個濱河市魁衙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌株搔,老刑警劉巖剖淀,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異邪狞,居然都是意外死亡祷蝌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門帆卓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巨朦,“玉大人,你說我怎么就攤上這事剑令『龋” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵吁津,是天一觀的道長棚蓄。 經(jīng)常有香客問我,道長碍脏,這世上最難降的妖魔是什么梭依? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮典尾,結(jié)果婚禮上役拴,老公的妹妹穿的比我還像新娘。我一直安慰自己钾埂,他們只是感情好河闰,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布科平。 她就那樣靜靜地躺著,像睡著了一般姜性。 火紅的嫁衣襯著肌膚如雪瞪慧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天部念,我揣著相機與錄音弃酌,去河邊找鬼。 笑死印机,一個胖子當(dāng)著我的面吹牛矢腻,可吹牛的內(nèi)容都是我干的门驾。 我是一名探鬼主播射赛,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奶是!你這毒婦竟也來了楣责?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤聂沙,失蹤者是張志新(化名)和其女友劉穎秆麸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體及汉,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沮趣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坷随。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片房铭。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖温眉,靈堂內(nèi)的尸體忽然破棺而出缸匪,到底是詐尸還是另有隱情,我是刑警寧澤类溢,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布凌蔬,位于F島的核電站,受9級特大地震影響闯冷,放射性物質(zhì)發(fā)生泄漏砂心。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一蛇耀、第九天 我趴在偏房一處隱蔽的房頂上張望辩诞。 院中可真熱鬧,春花似錦蒂窒、人聲如沸躁倒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秧秉。三九已至褐桌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間象迎,已是汗流浹背荧嵌。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留砾淌,地道東北人啦撮。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像汪厨,于是被迫代替她去往敵國和親赃春。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350