Spring解決循環(huán)依賴(上)

本篇先嘗試自己實現(xiàn)一個解決循環(huán)依賴的方案,下篇分析Spring是如何解決的。

1. 什么是循環(huán)依賴?

所謂的循環(huán)依賴是指,A 依賴 B,B 又依賴 A,它們之間形成了循環(huán)依賴?;蛘呤?A 依賴 B,B 依賴 C,C 又依 賴 A。它們之間的依賴關(guān)系如下:


Spring循環(huán)依賴

2. Spring解決了什么?

首先要搞清楚,Spring提供的幾種注入方式。

2.1 構(gòu)造器注入

顧名思義,構(gòu)造方法注入,就是被注入對象可以通過在其構(gòu)造方法中聲明依賴對象的參數(shù)列表,讓外部(通常是IoC容器)知道它需要哪些依賴對象。

2.2 屬性注入

對于JavaBean對象來說,通常會通過setXXX()和getXXX()方法來訪問對應(yīng)屬性。這些setXXX()方法統(tǒng)稱為setter方法,getXXX()當然就稱為getter方法。通過setter方法,可以更改相應(yīng)的對象屬性,通過getter方法,可以獲得相應(yīng)屬性的狀態(tài)。所以,當前對象只要為其依賴對象所對應(yīng)的屬性添加setter方法,就可以通過setter方法將相應(yīng)的依賴對象設(shè)置到被注入對象中。

2.3 接口注入(平時用不到,可以不考慮)

相對于前兩種注入方式來說,接口注入沒有那么簡單明了。被注入對象如果想要IoC Service
Provider為其注入依賴對象,就必須實現(xiàn)某個接口。這個接口提供一個方法,用來為其注入依賴對象。
IoC Service Provider最終通過這些接口來了解應(yīng)該為被注入對象注入什么依賴對象。

2.4 Spring只解決了單例下屬性注入的循環(huán)依賴

直接上代碼,先看構(gòu)造器注入的。

package com.spring.beans;

import org.springframework.stereotype.Component;

@Component
public class ClassA {

    private ClassB classB;

    public ClassA(ClassB classB) {
        this.classB = classB;
    }
}

package com.spring.beans;

import org.springframework.stereotype.Component;

@Component
public class ClassB {

    private ClassA classA;

    public ClassB(ClassA classA) {
        this.classA = classA;
    }
}

測試類

package com.spring.beans;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.spring.beans");

    }

}

最后報錯了,可見Spring不能解決構(gòu)造器注入的循環(huán)依賴問題,記住這個報錯信息,以后出現(xiàn)該報錯都是循環(huán)依賴的問題。

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?

再來看屬性注入的。

package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassA {

    @Autowired
    private ClassB classB;

}
package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassB {

    @Autowired
    private ClassA classA;

}
package com.spring.beans;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.spring.beans");
    }

}

運行成功。

3. 自己實現(xiàn)一個

3.1 第一版

3.1.1 解決思路

先上流程圖。


第一版

3.1.2 具體代碼

package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassA {

    @Autowired
    private ClassB classB;

    public ClassA(){
        System.out.println("ClassA實例化");
    }

    public ClassA(ClassB classB) {
        this.classB = classB;
    }

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

    public ClassB getClassB() {
        return classB;
    }

    public void sayHi(){
        System.out.println("我是classA");
    }
}
package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ClassB {

    @Autowired
    private ClassA classA;

    public ClassB(){
        System.out.println("ClassB實例化");
    }

    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public ClassA getClassA() {
        return classA;
    }

    public void sayHi(){
        System.out.println(classA);
    }
}
package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.Field;
import java.util.Map;

public class Demo {

    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentReferenceHashMap<>(10);

    private static Map<String, Object> singletonObjects = new ConcurrentReferenceHashMap<>(10);

    public static void main(String[] args) throws Exception {

        loadBeanDefinition();

        for(String beanName : beanDefinitionMap.keySet()){
            getBean(beanName);
        }

        ClassA classA = (ClassA) getBean("classA");

        classA.sayHi();
    }

    private static void loadBeanDefinition() {
        //生成Bean定義
        RootBeanDefinition rootBeanDefinitionClassA = new RootBeanDefinition(ClassA.class);
        RootBeanDefinition rootBeanDefinitionClassB = new RootBeanDefinition(ClassB.class);

        //將類注冊到Bean定義的Map里
        beanDefinitionMap.put("classA", rootBeanDefinitionClassA);
        beanDefinitionMap.put("classB", rootBeanDefinitionClassB);
    }

    @SuppressWarnings("")
    private static Object getBean(String beanName) throws Exception {

        //如果緩存里邊有,直接返回
        if (singletonObjects.containsKey(beanName)){
            return singletonObjects.get(beanName);
        }

        //實例化
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);

        Class<?> beanClass = beanDefinition.getBeanClass();
        
        //緩存里沒有的話,通過無參構(gòu)造函數(shù),將對象本身實例化
        Object beanInstance = beanClass.newInstance();

        //放入緩存
        singletonObjects.put(beanName,beanInstance);

        //屬性賦值,解析Autowired
        //拿到所有的屬性名
        Field[] declaredFields = beanClass.getDeclaredFields();

        //循環(huán)所有屬性
        for (Field declaredField : declaredFields){
            declaredField.setAccessible(true);

            //從屬性上拿到@Autowired
            Autowired annotation = declaredField.getAnnotation(Autowired.class);

            if (annotation != null){
                String name = declaredField.getName();
                Object fieldObject = getBean(name);
                declaredField.set(beanInstance,fieldObject);
            }
        }

        return  beanInstance;

    }

}

3.2 第二版

第一版實現(xiàn)完了,雖然在單線程下沒有循環(huán)依賴的問題了,但是緩存只存入了還沒有賦值的實例,即這個實例是不完整的,所以還需要用另一個緩存來存儲不完整的實例,用以區(qū)分完整的和不完整的實例。

3.2.1 流程圖

第二版

3.2.2 具體代碼

在原有代碼基礎(chǔ)上,做了小的改動,并封裝了一些方法。

package com.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.Field;
import java.util.Map;

public class Demo {

    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentReferenceHashMap<>(10);

    private static Map<String, Object> singletonObjects = new ConcurrentReferenceHashMap<>(10);

    private static Map<String, Object> earlySingletonObjects = new ConcurrentReferenceHashMap<>(10);

    public static void main(String[] args) throws Exception {

        loadBeanDefinition();

        for (String beanName : beanDefinitionMap.keySet()) {
            getBean(beanName);
        }

        ClassA classA = (ClassA) getBean("classA");

        classA.sayHi();
    }

    private static void loadBeanDefinition() {
        //生成Bean定義
        RootBeanDefinition rootBeanDefinitionClassA = new RootBeanDefinition(ClassA.class);
        RootBeanDefinition rootBeanDefinitionClassB = new RootBeanDefinition(ClassB.class);

        //將類注冊到Bean定義的Map里
        beanDefinitionMap.put("classA", rootBeanDefinitionClassA);
        beanDefinitionMap.put("classB", rootBeanDefinitionClassB);
    }

    @SuppressWarnings("")
    private static Object getBean(String beanName) throws Exception {

        Object object = getSingletonObject(beanName);

        if (object != null) {
            return object;
        }

        Object beanInstance = createBeanInstance(beanName);

        //放入一級緩存
        singletonObjects.put(beanName, beanInstance);

        return beanInstance;

    }

    /**
     * 負責Bean的實例化
     * @param beanName 對象的名字
     * @return 對象的實例
     * @throws Exception 異常
     */
    private static Object createBeanInstance(String beanName) throws Exception {
        //實例化
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);

        Class<?> beanClass = beanDefinition.getBeanClass();

        //緩存里沒有的話,通過無參構(gòu)造函數(shù),將對象本身實例化
        Object beanInstance = beanClass.newInstance();

        //放入二級緩存
        earlySingletonObjects.put(beanName, beanInstance);

        //屬性賦值,解析Autowired
        //拿到所有的屬性名
        Field[] declaredFields = beanClass.getDeclaredFields();

        //循環(huán)所有屬性
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);

            //從屬性上拿到@Autowired
            Autowired annotation = declaredField.getAnnotation(Autowired.class);

            if (annotation != null) {
                String name = declaredField.getName();
                Object fieldObject = getBean(name);
                declaredField.set(beanInstance, fieldObject);
            }
        }

        return beanInstance;
    }

    /**
     * 獲取單例對象
     * @param beanName 對象名字
     * @return 對象實例
     */
    private static Object getSingletonObject(String beanName) {
        //先從一級緩存中拿
        Object bean = singletonObjects.get(beanName);

        //一級緩存沒有,從二級緩存拿
        if (bean == null){
            bean = earlySingletonObjects.get(beanName);
            if (bean ==null){
                return null;
            }
        }

        return bean;
    }

}

至此,也算解決了單線程下的循環(huán)依賴問題,但是Spring作為一個開源框架,會有很多的擴展點以及使用場景,比如AOP、多線程環(huán)境下的使用,那么考慮這兩點的話,上邊代碼就不足以解決這些問題了,敬請期待下篇。

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

相關(guān)閱讀更多精彩內(nèi)容

  • [toc] 循環(huán)依賴 循環(huán)依賴就是N個類中循環(huán)嵌套引用,如果日常開發(fā)中我們用new對象的方式發(fā)生這種循環(huán)依賴的程序...
    星空怎樣閱讀 419評論 0 0
  • 1、什么是循環(huán)依賴 循環(huán)依賴就是循環(huán)引用,就是兩個或多個bean相互之間的持有對方,比如A引用B,而B又引用A,則...
    JBryan閱讀 714評論 0 0
  • 什么是循環(huán)依賴?在創(chuàng)建A的時候發(fā)現(xiàn)A中的屬性需要B對象,那就先去創(chuàng)建B對象,又發(fā)現(xiàn)B中的屬性需要A對象,那又去創(chuàng)建...
    Y了個J閱讀 2,064評論 0 0
  • 夜鶯2517閱讀 128,174評論 1 9
  • 版本:ios 1.2.1 亮點: 1.app角標可以實時更新天氣溫度或選擇空氣質(zhì)量,建議處女座就不要選了,不然老想...
    我就是沉沉閱讀 7,469評論 1 6

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