SpringBoot 启动报循环依赖问题
问题现象
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 61 common frames omitted
问题原因分析
原因:
我们知道,spring三级缓存一定程度上解决了循环依赖问题。A对象在实例化之后,属性赋值 【opulateBean(beanName,mbd,instanceWrapper)】执行之前,将ObjectFactory添加至三级缓存中,从而使得在B对象实例化后的属性赋值过程中,能从三级缓存拿到ObjectFactory,调用getobject0方法拿到A的引用,B由此能顺利完成初始化并加入到OC容器。此时A对象完成属性赋值之后,将会执行初始化 【initializeBean(beanName, exposedObject, mbd方法】,重点是@Async注解的处理正是在这地方完成的,其对应的后置处理器AsyncAnnotationBeanPostProcessor,在postProcessAfterlnitialization方法中将返回代理对象,此代理对象与B中持有的A对象引用不同,导致了以上报错。补充:
- context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值~
- context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值~
- 重点:此时因为A支持循环依赖,所以会执行A的getEarlyBeanReference方法得到它的早期引用。而执行getEarlyBeanReference()的时候因为@Async根本还没执行,所以最终返回的仍旧是原始对象的地址
- B完成初始化、完成属性的赋值,此时属性field持有的是Bean A原始类型的引用~
- 完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(...),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里~
- 尴尬场面出现了:B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象)
- 执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,so最终就抛错了~
- 代理的创建时机
- 初始化之后 (无循环依赖时)
- 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
- 代理的创建时机
解决方案
1)在A类上加@Lazy,保证A对象实例化晚于B对象;
2)在A类上使用@DependsOn({"b"})注解,保证A对象实例化晚于B对象(注意如果AB两个类相互DependsOn,也会形成循环依赖)
3)设置 AbstractAutowireCapableBeanFactory.setAllowRawInjectionDespiteWrappin(true),不会报错但会导致B持有的A引用不是最终的代理对象:
4)不使用@Async注解,通过自定义异步工具类发起异步线程;
参考
链接1:https://wenku.baidu.com/view/0bbb583b5aeef8c75fbfc77da26925c52dc59151.html
连接2:https://blog.csdn.net/yxh13521338301/article/details/117450763
SpringBoot 启动报循环依赖问题的更多相关文章
- SpringBoot启动报错Failed to determine a suitable driver class
SpringBoot启动报错如下 Error starting ApplicationContext. To display the conditions report re-run your app ...
- springboot 启动报错"No bean named 'org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry' available"
1.问题 springboot启动报错 "D:\Program Files\Java\jdk-11\bin\java.exe" -XX:TieredStopAtLevel=1 -n ...
- springboot bean的循环依赖实现 源码分析
springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...
- SpringBoot启动报错:ould not be registered. A bean with that name has already been defined in file and overriding is disabled.
SpringBoot启动报错 ***************************APPLICATION FAILED TO START*************************** Des ...
- springboot启动报错 Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
新建了一个springboot项目报一下错误: Failed to configure a DataSource: 'url' attribute is not specified and no em ...
- SpringBoot启动报:Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
使用spring boot对项目改造,启动报错: Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel m ...
- 折腾了我两天的springboot数据源datasource循环依赖问题,都被搞疯掉了
在做项目重构的时候增加了两个功能 1.多数据源. 2.token的验证从以前的数据库验证,移到了redis端. 1.多数据源使用 druid-spring-boot-starter 套件 其核心代码如 ...
- 《Springboot极简教程》问题解决:Springboot启动报错 Whitelabel Error Page: This application has no explicit mapping for(转)
13.2 Spring Boot启动报错:Whitelabel Error Page 13.2 Spring Boot启动报错:Whitelabel Error Page 问题描述 Whitelabe ...
- springboot启动报错:Failed to configure a DataSource
一.背景 springboot的出现,让项目搭建变得更方便快捷,同时简化掉很多的样板化配置代码,提高开发效率. 通过idea生成springboot项目,启动报错:Failed to configur ...
- springboot启动报错start bean 'eurekaAutoServiceRegistration' NullPointerException
解决方案参考:https://blog.csdn.net/hhj13978064496/article/details/82825365 我将eureka的依赖包放到了依赖包的最下面,启动报错, 如下 ...
随机推荐
- Springboot跨域配置的坑
部分时间需要加上crossOrigin
- 手把手教大家写书写一个Mqtt网关
摘要:物联网是现在比较热门的软件领域,众多物联网厂商都有自己的物联网平台,而物联网平台其中一个核心的模块就是Mqtt网关. 本文分享自华为云社区<一文带你掌握物联网mqtt网关搭建背后的技术原理 ...
- touchke变化值小的解决办法
方法一,提高主频 方法二,减小充电时间. 方法三,充电电流减半(具体看RM手册touchkey章节) 建议测试时采样值维持在3000-4000 其实以上操作就是增大Y轴间隔,以增大按下和未按下时的采样 ...
- Jenkins搭建与数据迁移实践
概述 本文主要介绍内容如下: 1.使用Docker搭建Jenkins 2.迁移原Jenkins数据到新搭建的Jenkins中 3.在Jenkins容器内部配置Maven的私服配置 4.在Jenkins ...
- 【踩坑记录】docker启动报错mountpoint for cgroup not found
具体报错信息: docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: start ...
- LeetCode_单周赛_327
目录 6283. 正整数和负整数的最大计数 代码 6285. 执行 K 次操作后的最大分数 代码 6284. 使字符串总不同字符的数目相等 代码 6283. 正整数和负整数的最大计数 代码 直接遍历统 ...
- 亲测有效! TG Pro 实时温度工具 V2.7.6 for mac 破解版
亲测有效! TG Pro 实时温度工具 V2.7.6 for mac 破解版 TG Pro (Temperature Gauge Pro) 是一款专业显示你的Mac实时温度的软件,可以在系统内的菜单栏 ...
- python70 前端框架之vue js的集中循环方式、key值的解释、input事件、v-model双向数据绑定、过滤案例、事件修饰符、按键修饰符、表单控制
js的几种循环方式 v-for可以循环的变量 可以循环的: 数组.数组带索引 对象.对象带key.value 字符串 字符串带索引 数字.数字带索引 <!DOCTYPE html> < ...
- java7.14
- React中组件之间是如何通信的 react的组件通信方式有哪些
一.是什么 通信指的是发送者通过某种媒体 以某种格式来传递信息 到收信者以达到某个目的,广义上,任何信息的传递都是通信 二.如何通信? 组件传递的方式有很多种,根据传送者和接收者可以分为如下: 父组件 ...