Spring解決循環依賴的關鍵spring
三級緩存的代碼以下:
緩存
bean中定義了切面、有@Transactional或@Async這類的加強註解,則必須在元素對象的基礎上加上增長邏輯,生成代理類異步
**注意只有一級緩存用了ConcurrentHashMap, 第二級、第三級都是使用了普通的hashmap,由於除了一級緩存,查找、新增和刪除二三級緩存節點的關鍵代碼使用同步代碼塊實現
事實上若是隻使用ConcurrentHashMap,不使用同步代碼塊反而會有問題,由於ConcurrentHashMap只能保證對節點單個操做的原子性、可見性,好比想上面先判斷沒有再新增就無法完成,get方法也不是一個絕對正確的結果。函數
那麼AService和BService完成初始化的過程是什麼樣的呢?post
這樣看好像只須要第一級緩存和第三級緩存就能夠解決循環依賴的問題了spa
其實使用三級緩存主要是在須要被代理的bean出現循環依賴的時候起做用.net
咱們先思考一下若是AService上定義了切面,這時候三級緩存中的構造Lambda返回的就不是原始對象了,而是被代理後的對象,這時候上面的流程中第4步中調用Lambda返回的就是AService的一個代理對象
假設AService一樣依賴CService,而CService也須要依賴AService,這樣AService填充進BService後,會接着填充CService, 使得CService也開始進行建立,CService的建立時須要注入AService,一樣能從三級緩存中找到工廠,調用工廠一樣返回AService的一個代理對象,這樣BService和CService建立過程都生成了一個AService的代理對象,並且持有的都不是同一個AService!代理
其實也比較簡單,上面的步驟中第4步BService從三級緩存中獲取到工廠生產代理對象時,把這個對象放到第二級緩存中,這樣CService須要注入AService時會一二三級緩存依次判斷,最後能在二級緩存中直接獲得代理對象,就不會去三級緩存中獲取工廠構建一個了code
其實生成代理對象通常仍是在執行BeanPostProcessor的這一步執行的,在這一步會根據原始對象生成代理對象放到單例池中
只用出現了循環依賴的時候纔會調用三級緩存中的工廠提早進行代理,上面的流程中第四步,BService填充AService時發現BSerice不在單例池中時候,就能夠評定必定是出現循環依賴了,接着在二級緩存中也找不到,因而開始找三級緩存中的工廠進行提早代理
可是要是以後BeanPostProcessor有生成一個代理對象放到單例池中,不是又出現了兩個代理對象了嗎?對象
spring是在執行提早代理加強時把原始對象又放到了一個專門用來作判斷是否已經進行了aop的map(earlyProxyReferences)裏面,lambda加強代碼以下:
在後置處理器執行aop的方法,先去這個map裏面去找這個對象,要是發現這個map裏面有這個bean了就再也不執行aop了
@Aysnc
註解的Bean的建立代理的時機特別特殊:
@EnableAsync開啓時會向容器注入AsyncAnnotationBeanPostProcessor,它是一個BeanPostProcessor,實現了postProcessAfterInitialization,具體的實如今其父類AbstractAdvisingBeanPostProcessor中,在設個PostProcessor會生成一個代理,就及完成了提早代理也會生成一個新的代理
一個類有同時有循環依賴提早代理和 @Async生成代理 的狀況時,會生成兩個不一樣的代理對象,一個是異步的代理對象,就會致使代碼在AbstractAutowireCapableBeanFactory.doCreateBean方法中直接拋出錯誤(最後一步會判斷二級緩存中的對象和Processor執行後的對象不一致時拋出異常)
解決辦法: