创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家!
在关于Spring的面试中,我们经常会被问到一个问题:Spring是如何解决循环依赖的问题的。这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够一下子思考出个中奥秘。
本文主要针对这个问题,从源码的角度对其实现原理进行讲解。
循环依赖的简单例子
比如几个Bean之间的互相引用:
甚至自己“循环”依赖自己:
原型(Prototype)的场景是不支持循环依赖的
先说明前提:原型(Prototype)的场景是不支持循环依赖的。单例的场景才能存在循环依赖。
原型(Prototype)的场景通常会走到 AbstractBeanFactory 类中下面的判断,抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A...
这就套娃了, Spring就先抛出了BeanCurrentlyInCreationException。
什么是原型(Prototype)的场景?
通过如下方式,可以将该类的bean设置为原型模式。
@Service
@Scope("prototype")
public class MyReportExporter extends AbstractReportExporter {
...
}
在Spring中,@Service默认都是单例的。
所谓单例,就是Spring的IOC机制只创建该类的一个实例,每次请求,都会用这同一个实例进行处理,因此若存在全局变量,本次请求的值肯定会影响下一次请求时该变量的值。
若不想影响下次请求,就需要用到原型模式,即@Scope(“prototype”)。
原型模式,指的是每次调用时,会重新创建该类的一个实例,比较类似于我们自己new的对
象实例。
具体例子:循环依赖的代码片段
我们先看看当时出问题的代码片段:
@Service
publicclass TestService1 {
@Autowired
private TestService2 testService2;
@Async
public void test1() {
}
}
@Service
publicclass TestService2 {
@Autowired
private TestService1 testService1;
public void test2() {
}
}
这两段代码中定义了两个Service类: TestService1 和 TestService2 ,在TestService1中注入了TestService2的实例,同时在TestService2中注入了TestService1的实例,这里构成了循环依赖。只不过,这不是普通的循环依赖,因为TestService1的test1方法上加了一个 @Async 注解。
大家猜猜程序启动后运行结果会怎样?
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService1': Bean with name 'testService1' has been injected into other beans [testService2] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type mat