Spring中的迴圈依賴
迴圈依賴就是迴圈引用,在spring中,就是兩個或者多個bean相互之間持有對方。如下圖,ClassA引用ClassB,ClassB引用ClassC,ClassC又引用ClassA,最終它們形成了一個環,這就是迴圈依賴。
Spring中的迴圈依賴
spring中將迴圈依賴分成了3中情況,分別是:
- 構造器迴圈依賴
- prototype範圍的依賴處理
- setter迴圈依賴
構造器迴圈依賴
通過構造器注入構成的迴圈依賴, 此依賴無法解決
。在Spring中會丟擲BeanCurrentlyInCreationException異常表示迴圈依賴。
對於構造器注入構成的迴圈依賴,在建立ClassA的時候,構造器需要ClassB,然後去建立ClassB,在建立ClassB的時候發現需要ClassA,形成了一個死迴圈,無法完成建立。
prototype範圍的依賴處理
對於 prototype
作用域的bean,spring容器無法完成依賴注入,因為spring不像快取單例那樣快取prototype作用域的bean。
setter迴圈依賴
表示通過setter注入方式構成的迴圈依賴,spring通過提前暴露構造器注入但未完成其他步驟(如setter操作)的bean來完成setter注入造成的迴圈依賴。
自己簡單的用程式碼來展示spring解決單例setter迴圈依賴的方式,具體spring中如何解決感興趣可以自己閱讀原始碼。
建立兩個迴圈依賴的類,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用來模仿Spring解決迴圈依賴獲取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 { /**用於快取正在初始化中的物件,同時作為提前暴露的快取*/ private static final Map<Class, Object> currentInitObjects = new ConcurrentHashMap<>(); /**用於快取初始化好的單例物件*/ private static final Map<Class, Object> objects = new ConcurrentHashMap<>(); /** * 獲取物件,並設值物件屬性。 * 1. 不考慮併發問題,簡單的示例 * 2. 解決單例setter迴圈依賴 * * @param cls * @param <T> * @return */ public <T> T getObject(Class<T> cls) { //如果已經初始化過直接返回 if (objects.containsKey(cls)) { return (T) objects.get(cls); } try { T t; //1. 簡單的使用建構函式建立物件,並提前暴露到currentInitObjects中 t = cls.newInstance(); //提前暴露到currentInitObjects中 currentInitObjects.put(cls, t); //2. 解決依賴屬性值 resolveDependence(t, cls); //3. 放入單例快取中 objects.put(cls, t); return t; } catch (Exception e) { System.out.println("初始化物件失敗:" + cls); return null; } finally { //4. 從正在初始化快取中移除 currentInitObjects.remove(cls); } } /** * 解決依賴屬性設值. * @param object 物件 * @param cls 物件class */ private void resolveDependence(Object object, Class cls) { //獲取物件的屬性,並進行賦值,省去了複雜的判斷,就認為是物件 //1.獲取所有屬性 Field[] fields = cls.getDeclaredFields(); //2.迴圈處理屬性值 Arrays.stream(fields).forEach(field -> { field.setAccessible(true); //2.1 獲取屬性class屬性 Class fieldClass = field.getType(); Object value; //2.2 判斷是否已經初始化過 if (objects.containsKey(fieldClass)) { value = objects.get(fieldClass); } else if (currentInitObjects.containsKey(fieldClass)) { //2.3 判斷當前初始化的類中有沒有這個屬性. value = currentInitObjects.get(fieldClass); } else { //2.4 如果都沒有,進行初始化 value = getObject(fieldClass); } //3. 使用反射設定屬性的值 try { field.set(object, value); } catch (IllegalAccessException e) { System.out.println("設定物件屬性失敗:" + cls + "-" + field.getName()); } }); } }
客戶端呼叫
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("-----我是分割線-----"); 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 -----我是分割線----- I am ClassA classB.getClassA() == classA:true classA.getClassB() == classB:true
從輸出可以發現:
- ClassA和ClassB都成功例項化
- 都是單例