本篇先嘗試自己實現(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)境下的使用,那么考慮這兩點的話婉弹,上邊代碼就不足以解決這些問題了睬魂,敬請期待下篇。