白话理解什么是Spring循环依赖


我们知道Spring IOC是指的容器来负责创建Bean并负责处理Bean之间的依赖关系,比如有如下两个类A、B:

@Component
public class A
	@Autowired
	private B b;

@Component
public class B

站在容器的角度,发现A依赖B,直接先创建B然后创建A,把B实例赋值给A的成员变量b,完事儿。

但如果是下面一种循环依赖的情况,按照上面的逻辑就不行了:

循环依赖
@Component
public class A
	@Autowired
	private B b;
	
@Component
public class B
	@Autowired
	private C c;
	
@Component
public class C
	@Autowired
	private A a;

A依赖B、B依赖C、C又依赖A

如果你是容器,尼玛到底该先创建谁??

解决思路

创建一个类实例之前,先去一个全局的缓存Map里看下是否有这个类的实例,如果有则说明已经创建过、直接返回,如果没有则创建。
创建类实例的对应的属性的实例、并赋值给成员变量。递归。
创建A的实例,加入缓存Map,遍历A的属性,发现B需要创建,创建B的实例也加入缓存,遍历B的属性发现需要创建C,创建C的实例,遍历C的属性发现需要创建A的实例,但是A实例已经在缓存了、直接赋值。循环结束。

下面按照上面的思路,写下代码模拟循环依赖的解决:

package com.wangan.cyclicdependency;

public class A {
	public B b;
}
public class B {
	public C c;
}
public class C {
	public A a;
}
package com.wangan.cyclicdependency;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * 模拟简单循环依赖解决
 * */
public class TestCyclicDependency {
	
	private static Map beanCache = new HashMap<>();
	
	public static void main(String[] args) throws Exception {
		String[] beanDefinations = {"com.wangan.cyclicdependency.A", 
					    "com.wangan.cyclicdependency.B", 
					    "com.wangan.cyclicdependency.C"};
		for(String beanName : beanDefinations) {
			getBean(beanName);
		}
		
		A a = (A) beanCache.get("com.wangan.cyclicdependency.A");	
		B b = (B) beanCache.get("com.wangan.cyclicdependency.B");
		C c = (C) beanCache.get("com.wangan.cyclicdependency.C");
		System.out.println(a.b);
		System.out.println(b.c);
		System.out.println(c.a);
		
		System.out.println(a.b==b);
		System.out.println(b.c==c);
		System.out.println(c.a==a);
	}
	
	public static Object getBean(String beanName) throws Exception {
		if(beanCache.containsKey(beanName)) {
			return beanCache.get(beanName);
		}else {
			Object bean = Class.forName(beanName).newInstance();
			beanCache.put(beanName, bean);
			Field[] fields = bean.getClass().getFields();
			for(Field f : fields) {
				String typeClassName = f.getType().getName();
				f.setAccessible(true);
				f.set(bean, getBean(typeClassName));
			}
			return bean;
		}
	}
	
}