当前位置: 首页 > news >正文

spring如何解决循环依赖

问题描述:spring中类A依赖类B,类B依赖类A。

对于这个问题,需要抓住关键的三个点:

1、创建类A的过程中,doCreateBean中的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));方法会在二级缓存中放入一个ObjectFaotory对象

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args){// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] 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 matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;
}

放入二级缓存中:

/*** Add the given singleton factory for building the specified singleton* if necessary.* <p>To be called for eager registration of singletons, e.g. to be able to* resolve circular references.* @param beanName the name of the bean* @param singletonFactory the factory for the singleton object*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}
}

2、三级缓存:一级singletonObjects,二级singletonFactories,三级earlySingletonObjects

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

一级singletonObjects,存放单例bean

二级singletonFactories,存放ObjectFactory类型工厂对象

三级earlySingletonObjects,存放半成品bean

3、然后执行populatBean方法,创建类B,开始走类B的创建流程:getSingleton,此方法是在createBean方法执行之前执行:

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

此方法会从首先从三级缓存earlySingletonObjects中取类A,取不到再二级缓存singletonFactories中取类A,可以取到。取出后放入三级缓存,earlySingletonObjects中,二级缓存删除。然后完成整个循环依赖的流程。

一些疑问:为什么首先从三级缓存中取?答:主要是为了性能。三级缓存中有A就不用从二级缓存找了。

为什么要有二级缓存?二级缓存的ObjectFactory有什么用?答:主要是为了使用aop代理。

为什么要从二级缓存中删除类A,放入三级缓存中?答:对于A,B,C三个类互相依赖的情况,B中找到A就放到三级缓存,下次C再次找A就不用取二级缓存中找了。

相关文章:

  • 嵌入式linux裸机调试之windows、linux联合gdb
  • Gromacs——使用过程中暴露问题分析及学习
  • docker-文件复制(docker cp:用于在Docker主机和容器之间拷贝文件或目录)
  • 数学建模研赛总结
  • 【Linux】tar 压缩使用绝对路径时解压会出现多级文件夹
  • 新手教学系列——用 VSCode 实现高效远程开发
  • linux查看进程所在的目录
  • 硬件设计基础之闲聊千兆以太网
  • C99中的变长数组
  • 在Mac电脑上安装adb环境
  • 开源实战分享 | 新书:《大型语言模型实战手册》随书代码分享
  • [大语言模型] 情感认知在大型语言模型中的近期进展-2024-09-26
  • 腾讯云新开端口
  • Python 读取与处理出入库 Excel 数据实战案例(HTML 网页展示)
  • 【LLM多模态】文生视频综述From Sora What We Can See: A Survey of Text-to-Video Generation
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【css3】浏览器内核及其兼容性
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • Flex布局到底解决了什么问题
  • Java编程基础24——递归练习
  • Java超时控制的实现
  • Java面向对象及其三大特征
  • learning koa2.x
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Netty 4.1 源代码学习:线程模型
  • spring cloud gateway 源码解析(4)跨域问题处理
  • vue脚手架vue-cli
  • Web标准制定过程
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 老板让我十分钟上手nx-admin
  • 模型微调
  • 如何编写一个可升级的智能合约
  • 【云吞铺子】性能抖动剖析(二)
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​Java基础复习笔记 第16章:网络编程
  • # 飞书APP集成平台-数字化落地
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • #每日一题合集#牛客JZ23-JZ33
  • (~_~)
  • (2022 CVPR) Unbiased Teacher v2
  • (3)STL算法之搜索
  • (7) cmake 编译C++程序(二)
  • (vue)页面文件上传获取:action地址
  • (差分)胡桃爱原石
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (七)Activiti-modeler中文支持
  • (转)创业的注意事项
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • .Net Core 笔试1
  • .net core开源商城系统源码,支持可视化布局小程序
  • .net FrameWork简介,数组,枚举