面试题:Spring如何解决循环依赖?
作为面试者,我很乐意分享关于Spring如何解决循环依赖的问题。
在Spring框架中,如果两个Bean之间存在相互依赖的关系,并且这种依赖是循环的,则可能会导致无法创建完整的对象图和出现循环依赖异常。为了解决这种类型的问题,Spring使用了两种技术:构造函数参数注入和三级缓存(三级缓存又称“早期对象”的缓存)。
- 构造函数参数注入
对于通过构造函数进行自动装配的Bean之间的循环依赖,可以使用构造函数参数注入来解决。Spring容器将创建Bean时所需的全部依赖项注入到构造函数中,并使用代理对象封装正在创建的Bean,从而避免了循环依赖。例如:
public class ProductService { private final ProductRepository productRepository; public ProductService(ProductRepository productRepository) { this.productRepository = productRepository; } } public class ProductRepository { private final ProductService productService; public ProductRepository(ProductService productService) { this.productService = productService; } }
在上述代码中,ProductService依赖于ProductRepository,而ProductRepository又依赖于ProductService。由于它们的依赖关系是循环的,因此可以使用构造函数注入来解决。
- 三级缓存技术
当Bean之间的循环依赖问题无法通过构造函数来处理时,Spring使用三级缓存来解决此类问题。在第一阶段,当创建一个Bean对象后,会先判断这个Bean是否已经缓存在singletonObjects这个单例池中了,如果是直接拿出来返回,如果没有就进入到第二阶段。在第二阶段,会将当前正在创建的Bean放入earlySingletonObjects这个早期单例列表中并设置其状态为“正在创建中”,然后继续创建其所依赖的其他Bean。在依赖的其他Bean都创建好后,在开始对当前Bean进行初始化之前,会将早期单例池中所有状态为“正在创建中”的Bean都提前暴露(替换)成代理对象,并更新它们的状态为“已创建”。由此,在对Bean进行初始化的过程中,该Bean从早期单例列表中获取到的代理对象,以满足该Bean对其它Bean的依赖,从而避免了循环依赖异常的发生。
总之,Spring框架可以通过构造函数参数注入与三级缓存技术来处理Bean之间相互依赖、且嵌套或循环的情况,保证依赖关系传递的正确性及完整性,从而符合java设计模式重要的“解耦”思想。