面试中常见Spring的面试题系列:Spring 如何解决循环依赖?
通常大部分人能说出来通过三级缓存来处理的。但如果深入点问,三级缓存的流程以及能否改为二级或者一级缓存处理 就无法说的很明白。
先来放一张图来举例循环依赖的场景:
代码示例:spring-learn: spring学习示例程序
也可以直接下载本地运行:spring-learn.zip-Java文档类资源-CSDN下载
代码示例目前采用的是xml+setter注入;后面增加field和构造器注入示例。
大致步骤为:
- 对象A进行实例化;
- 对象A实例化过程中,添加到单例工厂中;
- 对象A进行属性填充;
- 对象A属性填充处理ref=teacher的属性时确定需要依赖对象B;
- 对象B进行实例化;
- 对象B实例化过程中,添加到单例工厂中;
- 对象B进行属性填充;
- 对象B属性填充处理ref=student的属性时确定需要依赖对象A;
- 对象B属性填充过程,
- 先从singletonObjects中查找对象A(半成品,未初始化)是否存在,
- 然后再从earlySingletonObjects查找对象A(半成品,未初始化)是否存在,
- 最后通过singletonFactories查找对象A(半成品,未初始化),查到之后进行保存到earlySingletonObjects中;
- 跟第9步骤一起;
- 对象B进行初始化操作;
- 返回到对象A第3步的操作继续进行
- 对象A进行初始化操作;
- 最后对象A和对象B的实例化、初始化操作都完成了。
最近遇到Spring循环依赖相关的面试问题
1、原型模式下,Spring支持循环依赖吗?
答案:不支持,原型模式不会缓存对应的bean实例,自然不会进行循环依赖的处理,具体需要看源码部分,Spring直接抛出异常。
Spring版本:5.2.8
代码跟踪:AbstractBeanFactory#doGetBean 265-269行
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
2、构造器方式,Spring支持循环依赖吗?
答案:默认不支持,因为Spring循环依赖的处理是在属性填充阶段进行的,具体需要看源码部分,Spring直接抛出异常。
AbstractAutowireCapableBeanFactory#createBeanInstance构造器实例化,
ConstructorResolver#resolveAutowiredArgument解析构造器的参数,
AutowireCapableBeanFactory#resolveDependency解析依赖,
通过在构造器参数中标识@Lazy注解,Spring 生成并返回了一个代理对象是可以解决构造器循环依赖问题的。
Spring构造器注入循环依赖的解决方案是@Lazy,其基本思路是:对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完整的对象,
这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过TargetSource去拿到真实的对象[DefaultListableBeanFactory#doResolveDependency],然后通过反射完成调用。
------------欢迎各位留言交流,如有不正确的地方,请予以指正。【Q:981233589】