尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring 三级缓存解决循环依赖

Spring 三级缓存解决循环依赖 一、先明确基础前提1. 什么是循环依赖A 对象依赖 BB 对象又依赖 AComponent class A { Autowired B b; } Component class B { Autowired A a; }Spring 创建 Bean 默认步骤实例化new 对象堆中分配内存半成品对象属性还没注入属性填充Autowired注入依赖对象初始化PostConstruct、InitializingBean、init-method如果没有缓存 创建 A→实例化 A→注入 B→创建 B→实例化 B→注入 A此时 A 还没创建完成直接报错循环依赖。2. 三级缓存分别是什么Spring 用DefaultSingletonBeanRegistry里三个Map缓存名称数据类型存储内容作用时机一级缓存singletonObjectsMapString, Object完全初始化完毕、成品单例 BeanBean 全部流程走完后放入对外获取最终可用对象二级缓存earlySingletonObjectsMapString, Object提前暴露的半成品 Bean实例化完成未填充属性、未初始化解决循环依赖时临时存放半成品避免重复创建工厂三级缓存singletonFactoriesMapString, ObjectFactory?Bean 的对象工厂ObjectFactory内部持有半成品 BeanAOP 代理逻辑入口核心支持循环依赖同时兼容 AOP 动态代理关键只有单例 Bean、setter / 字段注入Autowired才能用三级缓存解决构造器注入无法自动解决。二、三级缓存整体工作流程A 依赖 BB 依赖 A步骤 1开始创建单例 A标记 A 正在创建singletonsCurrentlyInCreation集合标记防止并发重复创建实例化 Anew A ()得到一个属性为空的半成品 A把包装 A 的ObjectFactory放入三级缓存 singletonFactories// ObjectFactory.getObject()内部逻辑如果需要AOP代理就生成代理对象否则直接返回原生半成品A singletonFactories.put(a, () - getEarlyBeanReference(a, beanDefinition, 半成品A));这一步就是提前暴露半成品的入口。开始给 A 填充属性发现需要注入 B。步骤 2创建依赖 B标记 B 正在创建实例化 B半成品 BB 的 ObjectFactory 放入三级缓存填充 B 的属性发现需要注入 A。步骤 3B 去容器中查找 A执行getBean(a)先查一级缓存 singletonObjects无A 还没初始化完查二级缓存 earlySingletonObjects无查三级缓存 singletonFactories存在 A 对应的 ObjectFactory取出工厂执行getObject()如果 A 需要 AOP 代理此时直接生成代理对象不需要代理直接返回原生半成品 A将拿到的半成品 / 代理 A 放入二级缓存 earlySingletonObjects同时删除三级缓存中 A 的工厂三级只留还没被提前获取的工厂将二级缓存里的半成品 A 注入到 B 的属性中。步骤 4B 走完完整生命周期B 属性填充完成 → 执行初始化方法B 完全变成成品 Bean移入一级缓存 singletonObjectsB 创建完毕回到 A 的属性注入流程把成品 B 注入 A。步骤 5A 走完完整生命周期A 属性填充完毕执行初始化、后置处理器A 变为完整成品 Bean从二级缓存移除放入一级缓存清空标记singletonsCurrentlyInCreation循环依赖解除。三、为什么必须三级缓存二级缓存行不行方案 1只靠一级缓存完全不行一级只存成品创建中途拿不到半成品必然死循环报错。方案 2一级 二级缓存去掉三级二级直接存放原生半成品对象无 AOP 场景确实可以解决循环依赖有 AOP 代理时致命缺陷 Bean 原本需要最终生成代理对象如果提前把原生半成品注入给 BB 持有的是原生对象后续 A 再做 AOP 代理替换时B 引用不会同步更新最终 B 手里是原生对象A 对外是代理对象两个对象不一致。三级缓存的核心价值延迟生成代理正常情况Bean 走完生命周期末尾才由AbstractAutoProxyCreator生成代理循环依赖被迫提前暴露时通过ObjectFactory里的getEarlyBeanReference只有被循环依赖抓取时才临时生成代理保证不管是否提前暴露整个系统中只会有一个代理实例不会出现原生与代理共存错乱。ObjectFactory 内部关键方法protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject bean; // 判断是否需要AOP代理 if (!mbd.isSynthetic() hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof AbstractAutoProxyCreator) { // 提前创建代理 exposedObject ((AbstractAutoProxyCreator)bp).getEarlyBeanReference(bean, beanName); } } } return exposedObject; }四、三个缓存分工总结三级缓存 singletonFactories存放生产 Bean / 代理的工厂懒加载仅当发生循环依赖需要提前拿对象时才执行工厂方法平时不创建代理。二级缓存 earlySingletonObjects缓存已经通过三级工厂生成好的提前暴露对象原生 / 代理防止同一 Bean 被多处循环依赖反复调用工厂重复创建代理。一级缓存 singletonObjects最终稳定的成品单例所有初始化、代理全部完成业务正常获取的 Bean 都来自这里。五、哪些场景三级缓存无能为力构造器注入循环依赖构造器注入必须在实例化阶段就传入依赖此时还没机会把半成品放入三级缓存Spring 无法解决会直接抛出BeanCurrentlyInCreationException。 解决改成 setter/Autowired 字段注入、加Lazy延迟注入。原型prototypeBean 循环依赖原型不做缓存每次创建新对象没有缓存机制无法解决。多例之间互相依赖。六、面试高频精简回答版三级缓存分别是成品单例、提前暴露半成品、Bean 工厂核心思路实例化 Bean 后先把生成对象的工厂放进三级缓存提前占位依赖对方时从三级工厂取出半成品必要时提前生成 AOP 代理放入二级完成属性注入等 Bean 完整初始化后移入一级缓存三级缓存主要为了兼容 AOP 代理单纯解决循环依赖两级就够引入三级保证代理唯一性。
返回列表