1. 程式人生 > >Spring當中迴圈依賴很少有人講,今天一起來學習!

Spring當中迴圈依賴很少有人講,今天一起來學習!

網上關於Spring迴圈依賴的部落格太多了,有很多都分析的很深入,寫的很用心,甚至還畫了時序圖、流程圖幫助讀者理解,我看了後,感覺自己是懂了,但是閉上眼睛,總覺得還沒有完全理解,總覺得還有一兩個坎過不去,對我這種有點笨的人來說,真的好難。當時,我就在想,如果哪一天,我理解了Spring迴圈依賴,一定要用自己的方式寫篇部落格,幫助大家更好的理解,等我理解後,一直在構思,到底怎麼應該寫,才能更通俗易懂,就在前幾天,我想通了,這麼寫應該更通俗易懂。在寫本篇部落格之前,我翻閱了好多關於Spring迴圈依賴的部落格,網上應該還沒有像我這樣講解的,現在就讓我們開始把。 ## 什麼是迴圈依賴 一言以蔽之:兩者相互依賴。 在開發中,可能經常出現這種情況,只是我們平時並沒有注意到原來我們寫的兩個類、甚至多個類相互依賴了,為什麼注意不到呢?當然是因為沒有報錯,而且一點問題都木有,如果報錯了,或者產生了問題,我們還會注意不到嗎?這一切都是Spring的功勞,它在後面默默的為我們解決了迴圈依賴的問題。 如下所示: ```java @Configuration @ComponentScan public class AppConfig { } ``` ```java @Service public class AuthorService { @Autowired BookService bookService; } ``` ```java @Service public class BookService { @Autowired AuthorService authorService; } ``` ```java public class Main { public static void main(String[] args) { ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class); BookService bookService = (BookService) annotationConfigApplicationContext.getBean("bookService"); System.out.println(bookService.authorService); AuthorService authorService = (AuthorService) annotationConfigApplicationContext.getBean("authorService"); System.out.println(authorService.bookService); } } ``` 執行結果: ```java com.codebear.springcycle.AuthorService@63376bed com.codebear.springcycle.BookService@4145bad8 ``` 可以看到BookService中需要AuthorService,AuthorService中需要BookService,類似於這樣的就叫迴圈依賴,但是神奇的是竟然一點問題沒有。 當然有些小夥伴可能get不到它的神奇之處,至於它的神奇之處在哪裡,我們放到後面再說。 ## 任何迴圈依賴,Spring都能解決嗎 不行。 如果是原型 bean的迴圈依賴,Spring無法解決: ```java @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class BookService { @Autowired AuthorService authorService; } @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class AuthorService { @Autowired BookService bookService; } ``` 啟動後,令人恐懼的紅色字型在控制檯出現了: ```java image.png ``` 如果是構造引數注入的迴圈依賴,Spring無法解決: ```java @Service public class AuthorService { BookService bookService; public AuthorService(BookService bookService) { this.bookService = bookService; } } ``` ```java @Service public class BookService { AuthorService authorService; public BookService(AuthorService authorService) { this.authorService = authorService; } } ``` 還是討厭的紅色字型: ```java image.png ``` **迴圈依賴可以關閉嗎** 可以,Spring提供了這個功能,我們需要這麼寫: ```java public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.setAllowCircularReferences(false); applicationContext.register(AppConfig.class); applicationContext.refresh(); } } ``` 再次執行,就報錯了: ```java image.png ``` 需要注意的是,我們不能這麼寫: AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); applicationContext.setAllowCircularReferences(false); 如果你這麼寫,程式執行完第一行程式碼,整個Spring容器已經初始化完成了,你再設定不允許迴圈依賴,也於事無補了。 ## 可以迴圈依賴的神奇之處在哪 有很多小夥伴可能並不覺得可以迴圈依賴有多麼神奇,那是因為不知道矛盾點在哪,接下來就來說說這個問題: 當beanA,beanB迴圈依賴: 建立beanA,發現依賴beanB; 建立beanB,發現依賴beanA; 建立beanA,發現依賴beanB; 建立beanB,發現依賴beanA。 ... 好了,死迴圈了。 迴圈依賴的矛盾點就在於要建立beanA,它需要beanB,而建立beanB,又需要beanA,然後兩個bean都建立不出來。 ## 如何簡單的解決迴圈依賴 如果你曾經看過Spring解決迴圈依賴的部落格,應該知道它其中有好幾個Map,一個Map放的是最完整的物件,稱為singletonObjects,一個Map放的是提前暴露出來的物件,稱為earlySingletonObjects。 在這裡,先要解釋下這兩個東西: singletonObjects:單例池,其中存放的是經歷了Spring完整生命週期的bean,這裡面的bean的依賴都已經填充完畢了。 earlySingletonObjects:提前暴露出來的物件的map,其中存放的是剛剛創建出來的物件,沒有經歷Spring完整生命週期的bean,這裡面的bean的依賴還未填充完畢。 我們可以這麼做: 當我們建立完beanA,就把自己放到earlySingletonObjects,發現自己需要beanB,然後就去屁顛屁顛建立beanB; 當我們建立完beanB,就把自己放到earlySingletonObjects,發現自己需要beanA,然後就去屁顛屁顛建立beanA; 建立beanA前,先去earlySingletonObjects看一下,發現自己已經被創建出來了,把自己返回出去; beanB拿到了beanA,beanB建立完畢,把自己放入singletonObjects; beanA可以去singletonObjects拿到beanB了,beanA也建立完畢,把自己放到singletonObjects。 整個過程結束。 下面讓我們來實現這個功能: 首先,自定義一個註解,欄位上打上這個註解的,說明需要被Autowired: ```java @Retention(RetentionPolicy.RUNTIME) public @interface CodeBearAutowired { } ``` 再建立兩個迴圈依賴的類: ```java public class OrderService { @CodeBearAutowired public UserService userService; } public class UserService { @CodeBearAutowired public OrderService orderService; } ``` 然後就是核心,建立物件,填充屬性,並解決Spring迴圈依賴的問題: ```java public class Cycle { // 單例池,裡面放的是完整的bean,已完成填充屬性 private f