spring循環依賴

這是我參與8月更文挑戰的第6天,活動詳情查看:8月更文挑戰java

晚上好,我是盧卡,對於不少新手,項目中剛開始使用的框架就是spring,可是spring你到底瞭解多少,對於數據操做的底層對象調用,如何減小重複造輪子,這些都是咱們要掌握的問題。本期,咱們就利用spring中如何解決,依賴注入(Dependency Injection),循環依賴怎麼解決;程序員

循環依賴面試

循環依賴,一般是指Bean對象之間的互相依賴,好比A對象建立須要B對象,也就是A依賴於B,依賴類推,B依賴於C,C依賴於A, 造成一個完整的閉環,如下圖爲:spring

多個bean之間互相依賴,造成一個閉環====>循環依賴
複製代碼

說說spring容器中的循環依賴:緩存

必定是默認的單例bean中,屬性互相引用的場景,也就是說 spring初始化容器方法refresh()中,對待bean,都是初始化後的單例對象==>也就是scope=singleton
複製代碼

每一個對象是單例的對象,互相依賴的時候,能夠直接調用,markdown

spring底層怎麼保證AB循環依賴的問題?app

因此說,對於循環屢次依賴,咱們要求的是,單例且set注入------>能夠避免BeanCurrentlyInCreationException的問題框架

spring循環依賴的注入方式oop

對於spring循環依賴中,有一個誤區,一般能夠分爲構造器注入和set方法注入,兩種注入方式會有影響,咱們來找尋官網中的理解;
複製代碼

實例化構造器注入帶來的beancurrentlyIncreationException的問題;模擬A和B兩個實例對象,在循環依賴中,利用構造器互相注入,看可否實現循環依賴:post

建立三個類:

package com.atguowang.thirdinterview.spring.aroudyilai.constructor;

import org.springframework.stereotype.Component;

/**

  • @author shkstart
  • @create 2020-11-12 16:51*/@Componentpublic class ServiceA {
  • private ServiceB serviceB;
  • public ServiceA(ServiceB serviceB) {//當前私有的serviceB--this.serviceB=serviceB;}

}

package com.atguowang.thirdinterview.spring.aroudyilai.constructor;

import org.springframework.stereotype.Component;

/**

  • @author shkstart
  • @create 2020-11-12 16:51*/@Componentpublic class ServiceB {
  • private ServiceA serviceA;
  • public ServiceB(ServiceA serviceA ) {this.serviceA=serviceA;}}

package com.atguowang.thirdinterview.spring.aroudyilai.constructor;

import sun.applet.Main;

/**

  • @author shkstart
  • @create 2020-11-12 16:51*/public class TestConstructor {public static void main(String[] args) {
  • }

}

結論:

由於咱們要建立一個A,就要在構造器加載過程當中,加入一個B,可是又 要重複建立A,和B;constructor沒法解決這個問題;

2:利用set注入進行spring的循環依賴注入package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;

import org.springframework.stereotype.Component;

/**

  • @author lucas
  • @create 2020-12-12 17:13*/@Componentpublic class ServiceBB {
  • private ServiceAA serviceAA;
  • public void setServiceAA(ServiceAA serviceAA){this.serviceAA=serviceAA;System.out.println("B中獲得這個方法A的賦值");}}

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;

import org.springframework.stereotype.Component;

/**

  • @author lucas
  • @create 2020-12-12 17:13*/@Componentpublic class ServiceBB {
  • private ServiceAA serviceAA;
  • public void setServiceAA(ServiceAA serviceAA){this.serviceAA=serviceAA;System.out.println("B中獲得這個方法A的賦值");}}

循環依賴set方法注入,能夠依賴實現;

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;

import sun.java2d.pipe.AAShapePipe;

/**

  • @author lucas
  • @create 2020-12-12 17:13*/public class TestSetInjection {
  • public static void main(String[] args) {/*** 測試set方法注入的循環依賴*/ServiceAA aa = new ServiceAA();ServiceBB bb = new ServiceBB();
  • }

}

上述是基於JAVA SE實現的代碼,可是咱們要尋找底層spring容器中,到底對於循環依賴怎麼實現呢?

 咱們先肯定一個概念,就是spring容器化;
複製代碼

spring容器化

能夠把spring容器化,也就是存放bean對象,(實例化對象)能夠相互複用的一個池化思想,舉個例子,我每次要去買魚和雞肉,可是這個是倆對象,存在於兩個菜市場,spring容器就做爲中間點,把魚肉和雞肉都放入裏面, 而後我要使用的時候,就從池子中直接取用,這樣的好處是:
複製代碼

減小通信的資源損耗

節省對象複用

更好的控制對象

建立符合要求的初始化對象

spring做爲一個優秀的框架,極大程度上對於程序員來講,減小了建立對象,集中管理對象,複用等問題,作出了很大的貢獻,減小了重複造輪子,也就是說,咱們要建立一個對象的時候 ,咱們不用new ,而是經過權限控制,IOC(Inversion of Control)是指容器控制程序對象之間的關係,而不是傳統實現中,由程序代碼直接操控。控制權由應用代碼中轉到了外部容器,控制權的轉移給spring容器,更好的集中控制和管理對象;
複製代碼

咱們如今開始覬覦spring容器化來實現循環依賴到底怎麼玩?

這裏咱們就利用xml文件來實現,application.xml,兩個對象A和B,互相依賴
複製代碼

對象A:

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;

/**

  • @author lucas

  • @description spring容器中關於循環依賴的實現*/public class A {

  • private B b;

  • public B getB() {return b;}

  • public void setB(B b) {this.b = b;}

  • A(){System.out.println(" A -created- success");}

}

對象B:

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;

public class B {

private A a;

public A getA() {
    return a;
}

public void setA(A a) {
    this.a = a;
}

B(){

    System.out.println(" B -created- success");
}
複製代碼

}

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;

/**

  • @author Lucas

  • @description set方法實現*/

public class B {

private A a;

public A getA() {
    return a;
}

public void setA(A a) {
    this.a = a;
}

B(){

    System.out.println(" B -created- success");
}
複製代碼

}

建議一下,set方式注入是沒問題的,而後咱們在基礎上添加spring容器的配置文件,注意看好添加的位置;

application.xml:

開始作spring容器實例化建立兩個對象,進行循環依賴;

package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;

import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;

/**

  • @author Lucas
  • @description spring單例容器化實現循環依賴*/public class ClientSpringcontainer {public static void main(String[] args) {
  • }}
  • 注意事項:
  • 第一次開始的時候,可能會碰見bean不存在,注意創建時候配置文件的位置,檢查A和B的位置,我準備了連接,給你談談路,
  • 如何優雅的解決:

winterchen.blog.csdn.net/article/det…

思考一下,是由於spring容器化對象的時候,scope=singleton,單例對象,要是變爲scope=phototype ,(原型)也就是每次調用,都會建立一個新的A對象或者是B對象, 好漢們:試一把:咱們將配置文件的scope屬性改了;
複製代碼

結果如圖所示:

報出的錯誤,和咱們以前構造器注入方式一致,都是BeanCurrentlyInCreationException:bean對象一致處於建立內層的循環中,發生異常,也就說當scope屬性不是單例的時候,沒法解決spring循環依賴的問題;

其實作到這裏,咱們只是知道了,spring循環依賴是利用單例spring容器化,或者是set方式注入,實現具體的循環依賴(circular dependencies ),可是我仍是想知道,到底源碼底層是如何實現的;相應的我查到了新名詞,與spring循環依賴是有三級緩存實現的;



結論先記下:spring內部是根據三級緩存來實現循環依賴的;
複製代碼

三級緩存

當面試官說道,spring循環依賴,你能提到三級緩存,那證實你絕對是擁有內功的,這屬於源碼範疇,也能夠側面瞭解到你的學習潛力,哈哈,開幹:三級緩存主要是這個類; DefaultSingletonBeanRegistry;
複製代碼

IDEA的同窗呢,能夠那兩次shift鍵,查詢這個類的具體實現過程;

這個類中實際上是利用三個MAP,來實現三級緩存的;

 何爲三級緩存:
複製代碼

package org.springframework.beans.factory.support;public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {protected static final Object NULL_OBJECT = new Object();protected final Log logger = LogFactory.getLog(this.getClass());private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); //一級 單例-成品private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); //三級--工廠beanprivate final Map<String, Object> earlySingletonObjects = new HashMap(16); //二級 半成品

三級緩存只是適用於,對象是單例bean對象,每次建立bean對象;

注意事項:

在java中對於實例化和初始化是兩個概念:

    實例化對象以後-->才能初始化對象屬性



 實例化:  至關於自身須要請求一塊區域,可是隻是在申請成功的過程當中,其中放什麼仍是未知的數據



 初始化:咱們常說初始化--表明程序加載屬性,各類數據,開始運行的過程
複製代碼

scope=phototype

關於爲何scope=phototype時候,爲何會解決不了循環依賴?

原型對象-->多例模式,每次得到bean都會生成一個新的對象
複製代碼

解答:

對於每次建立一個對象bean,不是單例對象,因此不會進入三級緩存,也就不能經過三級緩存--->解決循環依賴問題

理解:

由於咱們第一次建立的對象是基於單例的,全部單例的對象走完了完整的生命週期--進入緩存--而後纔開始能夠解決循環依賴

因爲photoType每次要從新建立一個對象,因此沒法放入緩存中,也就不能使用三級緩存來解決循環依賴

寫到這裏,關於spring循環的問題就告一段落,其實還有下篇,集中對於如何對象bean實現三級緩存的細節,咱們來處理循環依賴,下節咱們接着幹,但願你會有所收穫,咱們一塊兒進步,相互成就纔是最好的狀態。

你們晚安,美夢,我是盧卡,感興趣就點個贊,再走吧

相關文章
相關標籤/搜索