本篇先尝试自己实现一个解决循环依赖的方案,下篇分析Spring是如何解决的。
1. 什么是循环依赖?
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依 赖 A。它们之间的依赖关系如下:
2. Spring解决了什么?
首先要搞清楚,Spring提供的几种注入方式。
2.1 构造器注入
顾名思义,构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让外部(通常是IoC容器)知道它需要哪些依赖对象。
2.2 属性注入
对于JavaBean对象来说,通常会通过setXXX()和getXXX()方法来访问对应属性。这些setXXX()方法统称为setter方法,getXXX()当然就称为getter方法。通过setter方法,可以更改相应的对象属性,通过getter方法,可以获得相应属性的状态。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。
2.3 接口注入(平时用不到,可以不考虑)
相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service
Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。
IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。
2.4 Spring只解决了单例下属性注入的循环依赖
直接上代码,先看构造器注入的。
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不能解决构造器注入的循环依赖问题,记住这个报错信息,以后出现该报错都是循环依赖的问题。
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. 自己实现一个
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();
//缓存里没有的话,通过无参构造函数,将对象本身实例化
Object beanInstance = beanClass.newInstance();
//放入缓存
singletonObjects.put(beanName,beanInstance);
//属性赋值,解析Autowired
//拿到所有的属性名
Field[] declaredFields = beanClass.getDeclaredFields();
//循环所有属性
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 第二版
第一版实现完了,虽然在单线程下没有循环依赖的问题了,但是缓存只存入了还没有赋值的实例,即这个实例是不完整的,所以还需要用另一个缓存来存储不完整的实例,用以区分完整的和不完整的实例。
3.2.1 流程图
3.2.2 具体代码
在原有代码基础上,做了小的改动,并封装了一些方法。
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();
//缓存里没有的话,通过无参构造函数,将对象本身实例化
Object beanInstance = beanClass.newInstance();
//放入二级缓存
earlySingletonObjects.put(beanName, beanInstance);
//属性赋值,解析Autowired
//拿到所有的属性名
Field[] declaredFields = beanClass.getDeclaredFields();
//循环所有属性
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;
}
}
至此,也算解决了单线程下的循环依赖问题,但是Spring作为一个开源框架,会有很多的扩展点以及使用场景,比如AOP、多线程环境下的使用,那么考虑这两点的话,上边代码就不足以解决这些问题了,敬请期待下篇。