是什么
循环依赖,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。
比如A依赖于B,B依赖于C,C又依赖于A。如下图:
代码中的体现
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
解决循环依赖的本质
先去缓存Map里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。
可以这么理解,当调用A的构造函数后,放入缓存Map中,在要注入属性B时。就去创建属性B,发现要注入属性A,从缓存中已经可以获取到创建好的A(虽然只是调用了构造函数,暴露了A的引用)。B创建好后返回创建A。这个时候循环依赖就解决了。
代码体现
public class Main {
private static Map<String, Object> cacheMap = new HashMap<>(4);
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 假装扫描出来的对象
Class[] classes = {A.class, B.class};
// 假装项目初始化实例化所有bean
for (Class aClass : classes) {
getBean(aClass);
}
// check
System.out.println(getBean(B.class).getA() == getBean(A.class));
System.out.println(getBean(A.class).getB() == getBean(B.class));
//在调用链可以是main->A->B
//不可以是main->A->B->A->....比如直接输出B
System.out.println(getBean(A.class).helllo());
}
private static <T> T getBean(Class<T> beanClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 本文用类名小写 简单代替bean的命名规则
String beanName = beanClass.getSimpleName().toLowerCase();
// 如果已经是一个bean,则直接返回
if (cacheMap.containsKey(beanName)) {
return (T) cacheMap.get(beanName);
}
// 将对象本身实例化
Object object = beanClass.getDeclaredConstructor().newInstance();
// 放入缓存
cacheMap.put(beanName, object);
// 把所有字段当成需要注入的bean,创建并注入到当前bean中
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 获取需要注入字段的class
Class<?> fieldClass = field.getType();
String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
// 如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可
// 如果缓存没有 继续创建
if (cacheMap.containsKey(fieldBeanName)){
field.set(object,cacheMap.get(fieldBeanName));
}else {
field.set(object,getBean(fieldClass));
}
}
// 属性填充完成,返回
return (T) object;
}
}
/**
* @author shunda wu
* @date 2020/8/10
**/
public class A {
private B b;
public A() {
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public String helllo(){
return b.helllo();
}
@Override
public String toString() {
return "A{" +
"b=" + b +
'}';
}
}
/**
* @author shunda wu
* @date 2020/8/10
**/
public class B {
private A a;
public B() {
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public String helllo(){
return "hello";
}
@Override
public String toString() {
return "B{" +
"a=" + a +
'}';
}
}