2023-01-19 07:20:34
Spring通过三级缓存机制解决单例作用域下的循环依赖问题,其核心是利用缓存提前暴露未完全初始化的Bean实例。以下是详细解析:
一、循环依赖的场景互相引用Bean A依赖Bean B,同时Bean B依赖Bean A:

自引用Bean A的属性依赖自身:

不支持的场景
原型作用域(Prototype):每次创建新实例,无法缓存,直接抛出BeanCurrentlyInCreationException。
构造器注入:Spring官方明确不支持,需改用属性注入。
Spring通过DefaultSingletonBeanRegistry类中的三个Map实现循环依赖的解决:
一级缓存:singletonObjects
存放完全初始化的单例Bean,即最终可用的Bean。
二级缓存:earlySingletonObjects
存放提前暴露的未完全初始化Bean(仅填充了属性,未执行初始化逻辑如init-method、Aware回调等)。
三级缓存:singletonFactories
存放Bean工厂对象(ObjectFactory),用于生成未完全初始化的Bean实例(通过getObject()方法)。
初始化Bean A
创建Bean A的原始对象(调用构造器),将其包装为ObjectFactory并存入三级缓存。
开始填充属性,发现依赖Bean B。
初始化Bean B
创建Bean B的原始对象,存入三级缓存。
填充Bean B的属性时,发现依赖Bean A。
从缓存获取Bean A
检查一级缓存:未找到(Bean A未初始化完成)。
检查二级缓存:未找到。
检查三级缓存:找到Bean A的ObjectFactory,调用getObject()获取未完全初始化的Bean A实例,并将其移入二级缓存(同时从三级缓存移除)。
完成Bean B初始化
将Bean B存入一级缓存,并注入到Bean A的属性中。
完成Bean A初始化
执行Bean A的剩余初始化逻辑(如init-method),完成后存入一级缓存。
流程图示:

三级缓存的作用
三级缓存(singletonFactories):解决AOP代理对象的循环依赖。若Bean需要代理,ObjectFactory会返回代理对象而非原始对象。
二级缓存(earlySingletonObjects):避免重复创建代理对象,提升性能。
为什么原型作用域不支持循环依赖
原型Bean每次创建新实例,无法缓存中间状态,会导致无限递归创建。
构造器注入的局限性
构造器注入时,Bean尚未实例化完成,无法提前暴露实例,因此无法支持循环依赖。
循环依赖的本质是对象图构建时的顺序问题,需通过中间状态缓存解决。类似算法题中的Two Sum问题:
示例代码(简化版):
Map<String, Object> cacheMap = new HashMap<>();public Object getBean(Class<?> beanClass) { String beanName = beanClass.getSimpleName().toLowerCase(); if (cacheMap.containsKey(beanName)) { return cacheMap.get(beanName); } // 1. 创建原始对象 Object instance = beanClass.getDeclaredConstructor().newInstance(); cacheMap.put(beanName, instance); // 存入一级缓存(模拟三级缓存的提前暴露) // 2. 填充属性 for (Field field : beanClass.getDeclaredFields()) { field.setAccessible(true); Class<?> fieldClass = field.getType(); String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); field.set(instance, cacheMap.containsKey(fieldBeanName) ? cacheMap.get(fieldBeanName) : getBean(fieldClass)); } return instance;}六、总结