日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

Spring循環(huán)依賴的解決辦法,你真的懂了嗎

瀏覽:24日期:2023-09-01 09:55:50

Spring循環(huán)依賴的解決辦法,你真的懂了嗎

介紹

先說一下什么是循環(huán)依賴,循壞依賴即循環(huán)引用,兩個或多個bean相互引用,最終形成一個環(huán)。Spring在初始化A的時候需要注入B,而初始化B的時候需要注入A,在Spring啟動后這2個Bean都要被初始化完成

Spring循環(huán)依賴的解決辦法,你真的懂了嗎

Spring的循環(huán)依賴有兩種場景

構(gòu)造器的循環(huán)依賴 屬性的循環(huán)依賴

構(gòu)造器的循環(huán)依賴,可以在構(gòu)造函數(shù)中使用@Lazy注解延遲加載。在注入依賴時,先注入代理對象,當首次使用時再創(chuàng)建對象完成注入

屬性的循環(huán)依賴主要是通過3個map來解決的

構(gòu)造器的循環(huán)依賴

@Componentpublic class ConstructorA { private ConstructorB constructorB; @Autowired public ConstructorA(ConstructorB constructorB) { this.constructorB = constructorB; }}

@Componentpublic class ConstructorB { private ConstructorA constructorA; @Autowired public 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,即上面這種形式的循環(huán)依賴Spring無法解決。

我們可以在ConstructorA或者ConstructorB構(gòu)造函數(shù)的參數(shù)上加上@Lazy注解就可以解決

@Autowiredpublic ConstructorB(@Lazy ConstructorA constructorA) {this.constructorA = constructorA;}

因為我們主要關(guān)注屬性的循環(huán)依賴,構(gòu)造器的循環(huán)依賴就不做過多分析了

屬性的循環(huán)依賴

先演示一下什么是屬性的循環(huán)依賴

@Componentpublic class FieldA { @Autowired private FieldB fieldB;}

@Componentpublic class FieldB { @Autowired private 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@3aa9e816 System.out.println(context.getBean(FieldA.class)); // com.javashitang.dependency.field.FieldB@17d99928 System.out.println(context.getBean(FieldB.class)); }}

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

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

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

bean的實例化過程,即調(diào)用構(gòu)造函數(shù)將對象創(chuàng)建出來 bean的初始化過程,即填充bean的各種屬性

bean初始化過程完畢,則bean就能被正常創(chuàng)建出來了

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

public interface ObjectFactory<T> { T getObject();}

public class DependencyDemo { // 初始化完畢的Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 正在初始化的Bean對應(yīng)的工廠,此時對象已經(jīng)被實例化 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(); // 已經(jīng)初始化好了,或者正在初始化 Object initObj = getSingleton(beanName, true); if (initObj != null) { return (T) initObj; } // bean正在被初始化 singletonsCurrentlyInCreation.add(beanName); // 實例化bean Object object = beanClass.getDeclaredConstructor().newInstance(); singletonFactories.put(beanName, () -> { return object; }); // 開始初始化bean,即填充屬性 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 獲取需要注入字段的class Class<?> fieldClass = field.getType(); field.set(object, getBean(fieldClass)); } // 初始化完畢 singletonObjects.put(beanName, object); singletonsCurrentlyInCreation.remove(beanName); return (T) object; } /** * allowEarlyReference參數(shù)的含義是Spring是否允許循環(huán)依賴,默認為true * 所以當allowEarlyReference設(shè)置為false的時候,當項目存在循環(huán)依賴,會啟動失敗 */ 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的循環(huán)依賴

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

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

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

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

public class DependencyDemo {// 初始化完畢的Beanprivate final Map<String, Object> singletonObjects =new ConcurrentHashMap<>(256);// 正在初始化的Bean對應(yīng)的工廠,此時對象已經(jīng)被實例化private final Map<String, ObjectFactory<?>> singletonFactories =new HashMap<>(16);// 緩存Bean對應(yīng)的工廠生產(chǎn)好的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();// 已經(jīng)初始化好了,或者正在初始化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參數(shù)的含義是Spring是否允許循環(huán)依賴,默認為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的實現(xiàn)和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)的實現(xiàn)一模一樣,這個方法幾乎所有分析Spring循環(huán)依賴的文章都會提到,這次你明白工作原理是什么了把

總結(jié)一波

拿bean的時候先從singletonObjects(一級緩存)中獲取 如果獲取不到,并且對象正在創(chuàng)建中,就從earlySingletonObjects(二級緩存)中獲取 如果還是獲取不到就從singletonFactories(三級緩存)中獲取,然后將獲取到的對象放到earlySingletonObjects(二級緩存)中,并且將bean對應(yīng)的singletonFactories(三級緩存)清除 bean初始化完畢,放到singletonObjects(一級緩存)中,將bean對應(yīng)的earlySingletonObjects(二級緩存)清除

參考博客

[1]https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ[2]https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw比較詳細[1]https://zhuanlan.zhihu.com/p/84267654[2]https://juejin.im/post/5c98a7b4f265da60ee12e9b2

到此這篇關(guān)于Spring循環(huán)依賴的解決辦法,你真的懂了嗎的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
91精品久久久久久久久久不卡| 国产精品一区二区三区四区在线观看| 日韩专区精品| 国产精品大片| 国产精品一区二区精品视频观看 | 国产日本精品| 欧美日韩夜夜| 蜜芽一区二区三区| 日本中文字幕视频一区| 欧美成人久久| 久久午夜精品| 日韩高清一区| 欧美国产极品| 欧美香蕉视频| 欧美日韩国产探花| 免费在线观看一区二区三区| 亚洲精品无播放器在线播放| 日韩精品一区二区三区中文字幕| 欧美亚洲二区| 高清在线一区| 国内精品99| 中文字幕日本一区| 国产福利资源一区| 亚洲精品.com| 好看的av在线不卡观看| 日本亚洲最大的色成网站www | 日韩精品亚洲专区| 老鸭窝一区二区久久精品| 日韩大片在线播放| 中文亚洲欧美| 久久精品99国产精品日本| 精品国产亚洲日本| 99久久九九| 欧美日韩精品一区二区三区视频 | 亚洲特级毛片| 亚洲在线免费| 国产精品毛片aⅴ一区二区三区| 免费看av不卡| 美国欧美日韩国产在线播放| 国产精品红桃| 久久精品国产大片免费观看| 玖玖玖国产精品| 国产福利资源一区| 国产精品97| 国产乱子精品一区二区在线观看| 鲁鲁在线中文| 亚洲综合精品| 亚洲另类av| 国产日韩中文在线中文字幕| 欧美日韩一区二区三区视频播放| 99精品99| 国产精品中文| 在线亚洲自拍| 久久精品理论片| 免播放器亚洲| 国产suv精品一区| 欧美综合国产| 97精品97| 免费人成在线不卡| 女生影院久久| 日本久久二区| 精品在线播放| 开心激情综合| 亚洲一级淫片| 亚洲国内精品| 欧美日韩xxxx| 午夜久久免费观看| 国产成人黄色| 欧美久久精品| 中国女人久久久| 色偷偷偷在线视频播放| 日韩欧美三区| 亚洲精品1区2区| 91九色综合| 香蕉久久国产| 999国产精品| 激情不卡一区二区三区视频在线| 亚洲精品国产日韩| 激情自拍一区| 国产不卡人人| 久久国产尿小便嘘嘘| 伊人精品一区| 国产精品伦理久久久久久| 欧美日韩伊人| 亚欧洲精品视频在线观看| 午夜国产精品视频| 日韩在线视频精品| 国产suv精品一区二区四区视频| 亚洲午夜免费| 91精品啪在线观看国产18| 精品一区二区三区在线观看视频| 只有精品亚洲| 欧美成人高清| 国产96在线亚洲| 国产麻豆一区二区三区| 四虎精品永久免费| 国产精品13p| 久久精品欧洲| 国产高清精品二区| 免费欧美日韩| 午夜电影亚洲| 国产综合精品| 国产精品久久| 国产视频一区免费看| 日本欧美不卡| 日韩电影二区| 久久精品伊人| 久久99偷拍| 开心激情综合| 国产精品99久久久久久董美香| 蜜桃视频一区二区| 国产精品成久久久久| 欧美激情aⅴ一区二区三区| 91久久精品无嫩草影院| 91精品丝袜国产高跟在线| 日韩精品视频在线看| 日韩精品视频网| 欧美一区免费| 国产伦理一区| 国产精品1luya在线播放| 国产精品久久久一区二区| 国产欧美日韩一区二区三区四区| 日韩av网站在线免费观看| 日韩av影院| 国产欧美激情| 精品入口麻豆88视频| 国产精品videosex极品| 精品国产乱码| 国产 日韩 欧美一区| 国产精品99一区二区| 尤物精品在线| 免费人成网站在线观看欧美高清| 免费在线看一区| 日韩av字幕| 久久精品国产一区二区| 97se综合| 99国产精品99久久久久久粉嫩| 蜜臀91精品一区二区三区| 亚洲毛片在线免费| 久久精品xxxxx| 国产精品成人3p一区二区三区| 中文字幕av亚洲精品一部二部 | 亚洲美洲欧洲综合国产一区| 尹人成人综合网| 亚洲精品影视| 国产精品啊啊啊| 狠狠久久伊人中文字幕| 国产另类在线| 国产黄大片在线观看| 国户精品久久久久久久久久久不卡| 国产偷自视频区视频一区二区| 综合欧美亚洲| 久久一区亚洲| 国产91欧美| 国产99久久| 美女被久久久| 国产黄色一区| 不卡一区2区| 久久精品av麻豆的观看方式| 日韩中文字幕91| 国产精品毛片久久久| 久久天堂成人| 日韩高清一区在线| 日韩欧美综合| 中文不卡在线| 日产精品一区二区| 香蕉久久国产| 蜜桃久久久久| 中文在线免费视频| 久久国产精品久久w女人spa| 国产精品白丝久久av网站| 精品丝袜在线| 天堂va在线高清一区| yellow在线观看网址| 天堂网在线观看国产精品| 亚洲制服欧美另类| 色乱码一区二区三区网站| 亚洲欧洲午夜| 精品国产中文字幕第一页| 9色精品在线| 国产精品一国产精品k频道56| 日韩美女一区二区三区在线观看| 水野朝阳av一区二区三区| 日韩av不卡一区二区| 欧美日韩精品免费观看视欧美高清免费大片 | 青青在线精品| 亚洲a一区二区三区| 久久国产麻豆精品| 婷婷激情一区| 日韩国产欧美视频| 久久国产成人午夜av影院宅| 久久国产精品免费一区二区三区| 欧美粗暴jizz性欧美20| 亚洲综合中文| 丝袜av一区| 国产精品黄色| 久久国产高清| 97精品国产福利一区二区三区| 国产一区成人| 欧美日韩在线观看首页|