日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

女朋友都能看懂,Spring如何解决循环依赖?

發布時間:2025/3/20 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 女朋友都能看懂,Spring如何解决循环依赖? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

先說一下什么是循環依賴,Spring在初始化A的時候需要注入B,而初始化B的時候需要注入A,在Spring啟動后這2個Bean都要被初始化完成

Spring的循環依賴有兩種場景

  • 構造器的循環依賴

  • 屬性的循環依賴

  • 構造器的循環依賴,可以在構造函數中使用@Lazy注解延遲加載。在注入依賴時,先注入代理對象,當首次使用時再創建對象完成注入

    屬性的循環依賴主要是通過3個map來解決的

    構造器的循環依賴

    @Component public?class?ConstructorA?{private?ConstructorB?constructorB;@Autowiredpublic?ConstructorA(ConstructorB?constructorB)?{this.constructorB?=?constructorB;} } @Component public?class?ConstructorB?{private?ConstructorA?constructorA;@Autowiredpublic?ConstructorB(ConstructorA?constructorA)?{this.constructorA?=?constructorA;} } @Configuration @ComponentScan("com.javashitang.dependency.constructor") public?class?ConstructorConfig?{ } public?class?ConstructorMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=new?AnnotationConfigApplicationContext(ConstructorConfig.class);System.out.println(context.getBean(ConstructorA.class));System.out.println(context.getBean(ConstructorB.class));} }

    運行ConstructorMain的main方法的時候會在第一行就報異常,說明Spring沒辦法初始化所有的Bean,即上面這種形式的循環依賴Spring無法解決。

    附上我歷時三個月總結的?Java 面試 + Java 后端技術學習指南,筆者這幾年及春招的總結,github 1.1k star,拿去不謝!下載方式1.?首先掃描下方二維碼 2.?后臺回復「Java面試」即可獲取

    我們可以在ConstructorA或者ConstructorB構造函數的參數上加上@Lazy注解就可以解決

    @Autowired public?ConstructorB(@Lazy?ConstructorA?constructorA)?{this.constructorA?=?constructorA; }

    因為我們主要關注屬性的循環依賴,構造器的循環依賴就不做過多分析了

    屬性的循環依賴

    先演示一下什么是屬性的循環依賴

    @Component public?class?FieldA?{@Autowiredprivate?FieldB?fieldB; } @Component public?class?FieldB?{@Autowiredprivate?FieldA?fieldA; } @Configuration @ComponentScan("com.javashitang.dependency.field") public?class?FieldConfig?{ } public?class?FieldMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=new?AnnotationConfigApplicationContext(FieldConfig.class);//?com.javashitang.dependency.field.FieldA@3aa9e816System.out.println(context.getBean(FieldA.class));//?com.javashitang.dependency.field.FieldB@17d99928System.out.println(context.getBean(FieldB.class));} }

    Spring容器正常啟動,能獲取到FieldA和FieldB這2個Bean

    屬性的循環依賴在面試中還是經常被問到的。總體來說也不復雜,但是涉及到Spring Bean的初始化過程,所以感覺比較復雜,我寫個demo演示一下整個過程

    Spring的Bean的初始化過程其實比較復雜,為了方便理解Demo,我就把Spring Bean的初始化過程分為2部分

  • bean的實例化過程,即調用構造函數將對象創建出來

  • bean的初始化過程,即填充bean的各種屬性

  • bean初始化過程完畢,則bean就能被正常創建出來了

    下面開始寫Demo,ObjectFactory接口用來生產Bean,和Spring中定義的接口一樣

    public?interface?ObjectFactory<T>?{T?getObject(); } public?class?DependencyDemo?{//?初始化完畢的Beanprivate?final?Map<String,?Object>?singletonObjects?=new?ConcurrentHashMap<>(256);//?正在初始化的Bean對應的工廠,此時對象已經被實例化private?final?Map<String,?ObjectFactory<?>>?singletonFactories?=new?HashMap<>(16);//?存放正在初始化的Bean,對象還沒有被實例化之前就放進來了private?final?Set<String>?singletonsCurrentlyInCreation?=Collections.newSetFromMap(new?ConcurrentHashMap<>(16));public??<T>?T?getBean(Class<T>?beanClass)?throws?Exception?{//?類名為Bean的名字String?beanName?=?beanClass.getSimpleName();//?已經初始化好了,或者正在初始化Object?initObj?=?getSingleton(beanName,?true);if?(initObj?!=?null)?{return?(T)?initObj;}//?bean正在被初始化singletonsCurrentlyInCreation.add(beanName);//?實例化beanObject?object?=?beanClass.getDeclaredConstructor().newInstance();singletonFactories.put(beanName,?()?->?{return?object;});//?開始初始化bean,即填充屬性Field[]?fields?=?object.getClass().getDeclaredFields();for?(Field?field?:?fields)?{field.setAccessible(true);//?獲取需要注入字段的classClass<?>?fieldClass?=?field.getType();field.set(object,?getBean(fieldClass));}//?初始化完畢singletonObjects.put(beanName,?object);singletonsCurrentlyInCreation.remove(beanName);return?(T)?object;}/***?allowEarlyReference參數的含義是Spring是否允許循環依賴,默認為true*?所以當allowEarlyReference設置為false的時候,當項目存在循環依賴,會啟動失敗*/public?Object?getSingleton(String?beanName,?boolean?allowEarlyReference)?{Object?singletonObject?=?this.singletonObjects.get(beanName);if?(singletonObject?==?null?&&?isSingletonCurrentlyInCreation(beanName))?{synchronized?(this.singletonObjects)?{if?(singletonObject?==?null?&&?allowEarlyReference)?{ObjectFactory<?>?singletonFactory?=this.singletonFactories.get(beanName);if?(singletonFactory?!=?null)?{singletonObject?=?singletonFactory.getObject();}}}}return?singletonObject;}/***?判斷bean是否正在被初始化*/public?boolean?isSingletonCurrentlyInCreation(String?beanName)?{return?this.singletonsCurrentlyInCreation.contains(beanName);}}

    測試一波

    public?static?void?main(String[]?args)?throws?Exception?{DependencyDemo?dependencyDemo?=?new?DependencyDemo();//?假裝掃描出來的對象Class[]?classes?=?{A.class,?B.class};//?假裝項目初始化所有beanfor?(Class?aClass?:?classes)?{dependencyDemo.getBean(aClass);}//?trueSystem.out.println(dependencyDemo.getBean(B.class).getA()?==?dependencyDemo.getBean(A.class));//?trueSystem.out.println(dependencyDemo.getBean(A.class).getB()?==?dependencyDemo.getBean(B.class)); }

    是不是很簡單?我們只用了2個map就搞定了Spring的循環依賴

    2個Map就能搞定循環依賴,那為什么Spring要用3個Map呢?

    原因其實也很簡單,當我們從singletonFactories中根據BeanName獲取相應的ObjectFactory,然后調用getObject()這個方法返回對應的Bean。在我們的例子中 ObjectFactory的實現很簡單哈,就是將實例化好的對象直接返回,但是在Spring中就沒有這么簡單了,執行過程比較復雜,為了避免每次拿到ObjectFactory然后調用getObject(),我們直接把ObjectFactory創建的對象緩存起來不就行了,這樣就能提高效率了

    比如A依賴B和C,B和C又依賴A,如果不做緩存那么初始化B和C都會調用A對應的ObjectFactory的getObject()方法。如果做緩存只需要B或者C調用一次即可。

    知道了思路,我們把上面的代碼改一波,加個緩存。

    public?class?DependencyDemo?{//?初始化完畢的Beanprivate?final?Map<String,?Object>?singletonObjects?=new?ConcurrentHashMap<>(256);//?正在初始化的Bean對應的工廠,此時對象已經被實例化private?final?Map<String,?ObjectFactory<?>>?singletonFactories?=new?HashMap<>(16);//?緩存Bean對應的工廠生產好的Beanprivate?final?Map<String,?Object>?earlySingletonObjects?=new?HashMap<>(16);//?存放正在初始化的Bean,對象還沒有被實例化之前就放進來了private?final?Set<String>?singletonsCurrentlyInCreation?=Collections.newSetFromMap(new?ConcurrentHashMap<>(16));public??<T>?T?getBean(Class<T>?beanClass)?throws?Exception?{//?類名為Bean的名字String?beanName?=?beanClass.getSimpleName();//?已經初始化好了,或者正在初始化Object?initObj?=?getSingleton(beanName,?true);if?(initObj?!=?null)?{return?(T)?initObj;}//?bean正在被初始化singletonsCurrentlyInCreation.add(beanName);//?實例化beanObject?object?=?beanClass.getDeclaredConstructor().newInstance();singletonFactories.put(beanName,?()?->?{return?object;});//?開始初始化bean,即填充屬性Field[]?fields?=?object.getClass().getDeclaredFields();for?(Field?field?:?fields)?{field.setAccessible(true);//?獲取需要注入字段的classClass<?>?fieldClass?=?field.getType();field.set(object,?getBean(fieldClass));}singletonObjects.put(beanName,?object);singletonsCurrentlyInCreation.remove(beanName);earlySingletonObjects.remove(beanName);return?(T)?object;}/***?allowEarlyReference參數的含義是Spring是否允許循環依賴,默認為true*/public?Object?getSingleton(String?beanName,?boolean?allowEarlyReference)?{Object?singletonObject?=?this.singletonObjects.get(beanName);if?(singletonObject?==?null&&?isSingletonCurrentlyInCreation(beanName))?{synchronized?(this.singletonObjects)?{singletonObject?=?this.earlySingletonObjects.get(beanName);if?(singletonObject?==?null?&&?allowEarlyReference)?{ObjectFactory<?>?singletonFactory?=this.singletonFactories.get(beanName);if?(singletonFactory?!=?null)?{singletonObject?=?singletonFactory.getObject();this.earlySingletonObjects.put(beanName,?singletonObject);this.singletonFactories.remove(beanName);}}}}return?singletonObject;} }

    我們寫的getSingleton的實現和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)的實現一模一樣,這個方法幾乎所有分析Spring循環依賴的文章都會提到,這次你明白工作原理是什么了把

    總結一波

  • 拿bean的時候先從singletonObjects(一級緩存)中獲取

  • 如果獲取不到,并且對象正在創建中,就從earlySingletonObjects(二級緩存)中獲取

  • 如果還是獲取不到就從singletonFactories(三級緩存)中獲取,然后將獲取到的對象放到earlySingletonObjects(二級緩存)中,并且將bean對應的singletonFactories(三級緩存)清除

  • bean初始化完畢,放到singletonObjects(一級緩存)中,將bean對應的earlySingletonObjects(二級緩存)清除

  • 最后,再附上我歷時三個月總結的?Java 面試 + Java 后端技術學習指南,筆者這幾年及春招的總結,github 1.1k star,拿去不謝!下載方式1.?首先掃描下方二維碼 2.?后臺回復「Java面試」即可獲取
    《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的女朋友都能看懂,Spring如何解决循环依赖?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 人人干人 | 日韩在线视频免费 | 亚洲人成电影网 | 免费日本黄色 | 日韩爱爱视频 | 91干干干| 国产一区二区三区高清视频 | 最近中文字幕无免费 | 久草热在线视频 | 天天操狠狠操夜夜操 | 五月天导航| 亚洲五级片| 亚洲熟伦熟女新五十路熟妇 | 日本久操视频 | 激情视频网站在线观看 | 亚洲精品视频一二三区 | 亚洲视频在线观看免费 | 久久精品国产亚洲av无码娇色 | 国产精品污污 | 中文字幕一区二区三区四区免费看 | 91看片国产 | 亚洲天堂伊人 | 夜操操| 久久在线免费观看视频 | 毛片毛片毛片毛片毛片 | 极品少妇av| av免费看网址 | 九九热精品在线观看 | 在哪看毛片 | 久久久久国产精品一区二区 | 一区国产视频 | 久热精品在线 | 免费一级特黄特色大片 | 欧美影院 | 国产精品xxx视频 | 白丝少妇 | 日本电影一区二区三区 | 俺来也俺也啪www色 欧洲一区二区视频 | 国产网站黄色 | 国产精品一区二区三区四区视频 | 无码乱人伦一区二区亚洲 | 日韩av在线看免费观看 | 亚洲性生活视频 | 伊人久久精品一区二区三区 | 综合久久综合久久 | 美女被草视频在线观看 | 91成人在线 | 一级中文字幕 | 欧美精品亚洲精品 | v天堂在线观看 | 人妻中文字幕一区二区三区 | 久久精彩免费视频 | 日韩av无码中文字幕 | 欧美aaa视频| 亚洲乱亚洲乱 | 午夜在线精品 | 国产欧美精品一区二区三区app | 特黄特色大片免费视频大全 | 日韩一级片免费 | 综合五月网 | 午夜性生活片 | 日本在线视频www | 欧美涩涩涩 | 亚洲无码一区二区三区 | 午夜激情影视 | 人妻互换免费中文字幕 | 午夜青青草 | 日本精品视频在线播放 | 草av| 又爽又黄又无遮挡 | 国产色综合视频 | 免费在线看黄色片 | cao久久| 伊人91在线 | 天堂一区二区三区 | 亚洲一区二区三区四区在线观看 | 丁五月 | 蜜臀久久久久久999 大陆熟妇丰满多毛xxxⅹ | 人与禽一级全黄 | 国产一区在线免费观看 | 日本在线播放视频 | 久久国产精品无码网站 | 日本少妇与黑人 | 国产片淫乱18一级毛片动态图 | 国产福利精品在线观看 | 欧美日韩黄色 | 少妇毛片一区二区三区 | 1024亚洲| 无码人妻精品一区二区三应用大全 | 男生插女生视频 | 成人免费激情视频 | 欧美乱码精品一区二区 | av无码久久久久久不卡网站 | 韩国久久久久 | 伊人艹 | 都市豪门艳霸淫美妇 | 十八岁世界在线观看高清免费韩剧 | 日本一区二区三区在线观看视频 | 成人午夜av在线 |