Spring源码解析之循环依赖

看个例子

CircularA类

@Service
public class CircularA {

    @Autowired
    private CircularB circularB;

    public CircularB getCircularB() {
        return circularB;
    }
}

CircularB类

@Service
public class CircularB {
    @Autowired
    private CircularA circularA;

    public CircularA getCircularA() {
        return circularA;
    }
}

测试方法

 @Test
    public void test3() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.com.dq");
        CircularA circularA = (CircularA) applicationContext.getBean("circularA");
        System.out.println(circularA.getCircularB());
        CircularB circularB = (CircularB) applicationContext.getBean("circularB");
        System.out.println(circularB.getCircularA());
    }

结果输出

 



上述示例是我们编码中很常见的一种编码方式

 

循环依赖详解

了解循环依赖,得先了解bean的实例化过程,bean的实例化请看作者写的spring源码解析之bean的实例化

循环依赖流程

 


 

总结

A B循环依赖,优先实例化完成的是B,再是A,因为A在DI的时候需要B的实例,B没有实例化就需要实例化,但是B在实例化的时候,DI的时候需要A,此时的A已经提前暴露,放在三级缓存中,所以我们能够拿到A的实例,然后注入到B中,B实例化完成以后,返回B的实例,然后A完成DI后,A就完成了实例化。

几点思考

多例为什么不能循环依赖?

因为多例情况下,A在实例化的时候,在依赖注入B的时候,B的实例有多个,依赖哪一个?
所以多例情况下,是不能循环依赖的
spring在源码中也做了处理,在缓存中拿不到实例后,else首先就判断了多例,多例的循环依赖直接会报错

if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

通过构造器注入的方式,能循环依赖吗?

看例子

CircularC 类
@Component
public class CircularC {

    private CircularD circularD;

    @Autowired
    public CircularC(CircularD circularD) {
        this.circularD = circularD;
    }
}
CircularD 类
@Component
public class CircularD {
    private CircularC circularC;

    @Autowired
    public CircularD(CircularC circularC) {
        this.circularC = circularC;
    }
}
执行结果

 



程序抛了异常
why?
C在实例化的时候,因为构造器的参数是引用类型,会触发beanFactory.getBean的操作,触发D类的实例化,此时D实例化,D实例化的时候同理会触发C的实例化,此时三个缓存中是没有任何的C的信息,因为三级缓存的暴露是在构造器实例化完成之后,所以在缓存中拿不到C,会继续走实例化流程,但是此时singletonsCurrentlyInCreation已经有了C,因为C在第一次实例化的时候,将beanName加入到了该容器中,此时就会抛出异常,源码如下:

 

protected void beforeSingletonCreation(String beanName) {
		//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

所以构造器的方式的依赖注入是不能循环依赖的

posted @ 2022-05-08 15:55  DengQ  阅读(128)  评论(0)    收藏  举报