Spring中的循環(huán)依賴(lài)

circle
閱讀原文請(qǐng)?jiān)L問(wèn)我的博客BrightLoong's Blog

什么是循環(huán)依賴(lài)

循環(huán)依賴(lài)就是循環(huán)引用,在spring中,就是兩個(gè)或者多個(gè)bean相互之間持有對(duì)方。如下圖,ClassA引用ClassB,ClassB引用ClassC,ClassC又引用ClassA,最終它們形成了一個(gè)環(huán),這就是循環(huán)依賴(lài)。

circle

Spring中的循環(huán)依賴(lài)

spring中將循環(huán)依賴(lài)分成了3中情況,分別是:

  • 構(gòu)造器循環(huán)依賴(lài)
  • prototype范圍的依賴(lài)處理
  • setter循環(huán)依賴(lài)

構(gòu)造器循環(huán)依賴(lài)

通過(guò)構(gòu)造器注入構(gòu)成的循環(huán)依賴(lài),此依賴(lài)無(wú)法解決。在Spring中會(huì)拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴(lài)。

對(duì)于構(gòu)造器注入構(gòu)成的循環(huán)依賴(lài),在創(chuàng)建ClassA的時(shí)候,構(gòu)造器需要ClassB,然后去創(chuàng)建ClassB,在創(chuàng)建ClassB的時(shí)候發(fā)現(xiàn)需要ClassA,形成了一個(gè)死循環(huán),無(wú)法完成創(chuàng)建。

prototype范圍的依賴(lài)處理

對(duì)于prototype作用域的bean,spring容器無(wú)法完成依賴(lài)注入,因?yàn)閟pring不像緩存單例那樣緩存prototype作用域的bean。

setter循環(huán)依賴(lài)

表示通過(guò)setter注入方式構(gòu)成的循環(huán)依賴(lài),spring通過(guò)提前暴露構(gòu)造器注入但未完成其他步驟(如setter操作)的bean來(lái)完成setter注入造成的循環(huán)依賴(lài)。

自己簡(jiǎn)單的用代碼來(lái)展示spring解決單例setter循環(huán)依賴(lài)的方式,具體spring中如何解決感興趣可以自己閱讀源碼。

創(chuàng)建兩個(gè)循環(huán)依賴(lài)的類(lèi),ClassA和ClassB。

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */
public class ClassA {

    private ClassB classB;

    public ClassB getClassB() {
        return classB;
    }

    public void setClassB(ClassB classB) {
        this.classB = classB;
    }

    public void say() {
        System.out.println("I am ClassA");
    }
}

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */
public class ClassB {

    private ClassA classA;

    public ClassA getClassA() {
        return classA;
    }

    public void setClassA(ClassA classA) {
        this.classA = classA;
    }

    public void say() {
        System.out.println("I am ClassB. Who are you?");
        classA.say();
    }
}

ObjectFactory用來(lái)模仿Spring解決循環(huán)依賴(lài)獲取bean

package io.github.brightloong.lab.spring.cyclicdependence;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */
public class ObjectFactory {

    /**用于緩存正在初始化中的對(duì)象,同時(shí)作為提前暴露的緩存*/
    private static final Map<Class, Object> currentInitObjects = new ConcurrentHashMap<>();

    /**用于緩存初始化好的單例對(duì)象*/
    private static final Map<Class, Object> objects = new ConcurrentHashMap<>();

    /**
     * 獲取對(duì)象,并設(shè)值對(duì)象屬性。
     * 1. 不考慮并發(fā)問(wèn)題,簡(jiǎn)單的示例
     * 2. 解決單例setter循環(huán)依賴(lài)
     *
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getObject(Class<T> cls) {
        //如果已經(jīng)初始化過(guò)直接返回
        if (objects.containsKey(cls)) {
            return (T) objects.get(cls);
        }
        try {
            T t;
            //1. 簡(jiǎn)單的使用構(gòu)造函數(shù)創(chuàng)建對(duì)象,并提前暴露到currentInitObjects中
            t = cls.newInstance();
            //提前暴露到currentInitObjects中
            currentInitObjects.put(cls, t);
            //2. 解決依賴(lài)屬性值
            resolveDependence(t, cls);
            //3. 放入單例緩存中
            objects.put(cls, t);
            return t;
        } catch (Exception e) {
            System.out.println("初始化對(duì)象失?。? + cls);
            return null;
        } finally {
            //4. 從正在初始化緩存中移除
            currentInitObjects.remove(cls);
        }
    }

    /**
     * 解決依賴(lài)屬性設(shè)值.
     * @param object 對(duì)象
     * @param cls 對(duì)象class
     */
    private void resolveDependence(Object object, Class cls) {
        //獲取對(duì)象的屬性,并進(jìn)行賦值,省去了復(fù)雜的判斷,就認(rèn)為是對(duì)象

        //1.獲取所有屬性
        Field[] fields = cls.getDeclaredFields();

        //2.循環(huán)處理屬性值
        Arrays.stream(fields).forEach(field -> {
            field.setAccessible(true);
            //2.1 獲取屬性class屬性
            Class fieldClass = field.getType();
            Object value;
            //2.2 判斷是否已經(jīng)初始化過(guò)
            if (objects.containsKey(fieldClass)) {
                value = objects.get(fieldClass);
            } else if (currentInitObjects.containsKey(fieldClass)) {
                //2.3 判斷當(dāng)前初始化的類(lèi)中有沒(méi)有這個(gè)屬性.
                value = currentInitObjects.get(fieldClass);
            } else {
                //2.4 如果都沒(méi)有,進(jìn)行初始化
                value = getObject(fieldClass);
            }
            //3. 使用反射設(shè)置屬性的值
            try {
                field.set(object, value);
            } catch (IllegalAccessException e) {
                System.out.println("設(shè)置對(duì)象屬性失?。? + cls + "-" + field.getName());
            }
        });
    }
}

客戶(hù)端調(diào)用

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */
public class Client {

    public static void main(String[] args) {
        ObjectFactory factory = new ObjectFactory();
        ClassB classB = factory.getObject(ClassB.class);
        classB.say();
        System.out.println("-----我是分割線(xiàn)-----");

        ClassA classA = factory.getObject(ClassA.class);
        classA.say();
        System.out.println("classB.getClassA() == classA:" + (classB.getClassA() == classA));

        System.out.println("classA.getClassB() == classB:" + (classA.getClassB() == classB));
    }
}

輸出如下:

I am ClassB. Who are you?
I am ClassA
-----我是分割線(xiàn)-----
I am ClassA
classB.getClassA() == classA:true
classA.getClassB() == classB:true

從輸出可以發(fā)現(xiàn):

  • ClassA和ClassB都成功實(shí)例化
  • 都是單例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容