spring循環依賴問題

	spring在DefaultSingletonBeanRegistry類中有以下幾種緩存池:
	/** Cache of singleton objects: bean name to bean instance. */
	//singletonObjects:key存bean name,value存bean實例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

/** Names of beans that are currently in creation. */
//singletonsCurrentlyInCreation存正在創建的bean名稱
private final Set<String> singletonsCurrentlyInCreation =
		Collections.newSetFromMap(new ConcurrentHashMap<>(16));

/** Names of beans currently excluded from in creation checks. */
private final Set<String> inCreationCheckExclusions =
		Collections.newSetFromMap(new ConcurrentHashMap<>(16));
類DefaultSingletonBeanRegistry:
/**
	 * Callback before singleton creation.
	 * 在單例bean創建之前嘗試回調
	 * <p>The default implementation register the singleton as currently in creation.
	 * 將單例bean註冊爲正在創建中的默認實現
	 * @param beanName the name of the singleton about to be created
	 * 參數爲將要創建的bean名稱
	 * @see #isSingletonCurrentlyInCreation
	 */
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

比如當A、B兩個service相互依賴的時候:

@Service
public class ServiceAImpl implements ServiceA {
    private ServiceB serviceB;

    public ServiceAImpl(ServiceB serviceB) {
        this.serviceB = serviceB;
    }


    public String print() {
        return "service a print ";
    }
}

@Service
public class ServiceBImpl implements ServiceB {

    private final ServiceA serviceA;

    public ServiceBImpl(ServiceA serviceA) {
        this.serviceA = serviceA;
    }

    public String print() {
        return serviceA.print() + "service b print ";
    }
}

將工程啓動,當spring創建bean時候,比如首先創建A:
在這裏插入圖片描述

可以看到首先將beanname serviceAImpl放到了singletonsCurrentlyInCreation,而由於serviceAImpl依賴了B,這時spring嘗試初始化serviceBImpl,又將serviceBImpl放到了singletonsCurrentlyInCreation,又發現serviceBImpl依賴了serviceAImpl,因此又會嘗試初始化serviceAImpl;再次來到beforeSingletonCreation方法:
在這裏插入圖片描述

注意這裏Set singletonsCurrentlyInCreation裏面已經包括了serviceAImpl和serviceBImpl,再次執行將導致!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)兩個條件同時滿足,直接導致了拋出異常throw new BeanCurrentlyInCreationException(beanName);

看一下BeanCurrentlyInCreationException
在這裏插入圖片描述

提示Requested bean is currently in creation: Is there an unresolvable circular reference?
說明spring確實檢查到了循環依賴,並且沒有辦法處理,只能拋出異常

如果將其中一個bean改成使用setter注入方式:


@Service
public class ServiceAImpl implements ServiceA {

    private  ServiceB serviceB;

    public String print() {
        return " service a print ";
    }

    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

再次執行:
在這裏插入圖片描述

首先將A將入到singletonsCurrentlyInCreation,然後往下走,在getSingleton方法中,serviceAImpl添加到了到了earlySingletonObjects中,也就是所謂的提前暴露。

在這裏插入圖片描述
等到再次回到beforeSingletonCreation時,準備創建B,這時發現singletonsCurrentlyInCreation裏面已經空了,而我們在registeredSingletons裏面發現了ServiceAImpl的蹤影:

在這裏插入圖片描述

說明當用setter方法注入的時候實際上會用默認無參構造函數將A實例化,而A持有的對象B將延後初始化,等到單例B真正被初始化後,纔將B的實例設置到A中。

如果將B改成setter注入也是一樣的結果。

結論:
如果兩個bean之間有循環依賴,並且都是用構造器方式注入時,由於spring要使用默認構造函數實例化bean,會將兩個bean都放到待創建set後然後拋異常。
而只要有至少一個bean初始化方式爲setter方式時,就可以使用無參構造函數先將bean初始化,等到所依賴的bean被構造出來後再執行set,也就解決了循環依賴的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章