本篇先嘗試自己實現(xiàn)一個解決循環(huán)依賴的方案,下篇分析Spring是如何解決的。
1. 什么是循環(huán)依賴?
所謂的循環(huán)依賴是指,A 依賴 B,B 又依賴 A,它們之間形成了循環(huán)依賴?;蛘呤?A 依賴 B,B 依賴 C,C 又依 賴 A。它們之間的依賴關(guā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)境下的使用,那么考慮這兩點的話,上邊代碼就不足以解決這些問題了,敬請期待下篇。