范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Spirng循环依赖报错Isthereanunresolvablecircularreference?

  1:前言
  最近在项目中遇到了一次循环依赖报错的问题,虽然解决的很快,但是有些不明白的地方,特此记录。
  在此我把 bean 的结构和 注入方式单独拎出来进行演示 1.1:报错提示
  1.2:错误日志Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name "brokenComponent": Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name "tubunComponent" defined in class path resource [com/dev/config/sprngcache3/TwConfig.class]: Unsatisfied dependency expressed through method "tubunComponent" parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name "denpaComponent": Unsatisfied dependency expressed through field "tolenComponent"; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name "tolenComponent": Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name "tubunComponent": Requested bean is currently in creation:Is there an unresolvable circular reference? 1.3:stack overflow问题描述
  Requested bean is currently in creation: Is there an unresolvable circular reference?
  点击进入 stack overflow 问题描述 1.4:bean 依赖结构
  BrokenComponent: @Component public class BrokenComponent {      @Autowired     private TolenComponent tolenComponent;     @Resource     private TubunComponent tubunComponent; }
  TolenComponent: @Transactional @Component public class TolenComponent {      @Resource     private TubunComponent tubunComponent;  }
  TubunComponent: public class TubunComponent {      private DenpaComponent denpaComponent;      public TubunComponent(DenpaComponent denpaComponent) {         this.denpaComponent = denpaComponent;     } }
  TwConfig: @Configuration public class TwConfig {      @Bean     public TubunComponent tubunComponent(DenpaComponent denpaComponent) {         return new TubunComponent(denpaComponent);     } }
  DenpaComponent: @Component public class DenpaComponent {      @Autowired     private TolenComponent tolenComponent; }
  除了 tubunComponent 是构造器注入,其他的bean 都是set 注入。
  分析bean依赖图可以发现确实是循环依赖的问题。 2:问题分析
  由于本人对spring bean 的加载机制不是很清晰,这次特意花了几天时间做了梳理。 2.1:spring 的 bean 加载过程
  2.2:spring 的三级缓存
  解决了什么问题?
  推荐博客:https://cloud.tencent.com/developer/article/1497692 2.3:Spring 容器加载过程中比较重要的类/属性
  类名称
  方法
  作用
  DefaultListableBeanFactory
  preInstantiateSingletons
  Trigger initialization of all non-lazy singleton beans.../ 触发所有非懒加载的单例bean进行初始化
  AbstractBeanFactory
  getBean/doGetBean
  从容器中获取bean
  DefaultSingletonBeanRegistry
  getSingleton
  从缓存中获取单例实例,没有则走单例的创建流程
  DefaultSingletonBeanRegistry
  beforeSingletonCreation
  创建单例之前会将单例放在set集合(singletonsCurrentlyInCreation)中,有检查bean是否被循环依赖的作用
  DefaultSingletonBeanRegistry
  beforeSingletonCreation
  创建单例之后会将单例从set集合(singletonsCurrentlyInCreation)中移除
  AbstractAutowireCapableBeanFactory
  createBean/doCreateBean
  创建单例bean
  AbstractAutowireCapableBeanFactory
  populateBean
  对bean进行属性赋值(往往是二级缓存中的bean)
  AbstractAutowireCapableBeanFactory
  initializeBean
  对属性赋值之后的bean进行初始化,加载RootBeanDefinition信息,这一步做完才是一个完整的可用的beanpublic class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {  	/** Cache of singleton objects: bean name to bean instance.(一级缓存,存放已初始化完毕的bean) */ 	private final Map singletonObjects = new ConcurrentHashMap<>(256);  	/** Cache of singleton factories: bean name to ObjectFactory.(三级缓存,存放未进行过初始化的beanFactory) */ 	private final Map> singletonFactories = new HashMap<>(16);  	/** Cache of early singleton objects: bean name to bean instance. (二级缓存,存放已实例化,但未进行装配过属性的bean)*/ 	private final Map earlySingletonObjects = new HashMap<>(16);  	/** Set of registered singletons, containing the bean names in registration order.(存放已注册了的单例集合 = singletonObjects.size + earlySingletonObjects)*/ 	private final Set registeredSingletons = new LinkedHashSet<>(256);  	/** Names of beans that are currently in creation.(存放正在被创建的实例,用于检查是否有循环依赖) */ 	private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));          .... } 2.4:注册后处理器->BeanPostProcessor
  作用:对通过 BeanFactory 创建的 bean 进行属性填充。
  这是AbstractAutowireCapableBeanFactory#populateBean 方法中的某一段代码: for (BeanPostProcessor bp : getBeanPostProcessors()) { 	if (bp instanceof InstantiationAwareBeanPostProcessor) { 		InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 		PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); 		if (pvsToUse == null) { 			if (filteredPds == null) { 				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); 			} 			pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); 			if (pvsToUse == null) { 				return; 			} 		} 		pvs = pvsToUse; 	} }
  我打出了所有要进行轮询校验的 BeanPostProcessor
  brokenComponent 中有两个bean:
  一个是通过@Resource 注解注入的,一个是通过@Autowire 进行注入的,奉劝各位同学千万不要两种混用啊!!! 2.4.1:CommonAnnotationBeanPostProcessor/**  * ...   *

The central element is the {@link javax.annotation.Resource} annotation * for annotation-driven injection of named beans, by default from the containing * Spring BeanFactory, with only {@code mappedName} references resolved in JNDI. * The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups * equivalent to standard Java EE 5 resource injection for {@code name} references * and default names as well. The target beans can be simple POJOs, with no special * requirements other than the type having to match. * *

The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too, * analogous to {@link javax.annotation.Resource} but with the capability of creating * specific JAX-WS service endpoints. This may either point to an explicitly defined * resource by name or operate on a locally specified JAX-WS service class. Finally, * this post-processor also supports the EJB 3 {@link javax.ejb.EJB} annotation, * analogous to {@link javax.annotation.Resource} as well, with the capability to * specify both a local bean name and a global JNDI name for fallback retrieval. * The target beans can be plain POJOs as well as EJB 3 Session Beans in this case. * ... */ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { ... @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); } return pvs; } }   通过翻译及反复debug验证,这个类将对某个bean中所有被 @Resource 注解修饰的属性进行填充。 2.4.2:AutowiredAnnotationBeanPostProcessor/** * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that autowires annotated fields, setter methods and arbitrary config methods. * Such members to be injected are detected through a Java 5 annotation: by default, * Spring"s {@link Autowired @Autowired} and {@link Value @Value} annotations. * *

Also supports JSR-330"s {@link javax.inject.Inject @Inject} annotation, * if available, as a direct alternative to Spring"s own {@code @Autowired}. * ... * @see Autowired * @see Value */ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { ... @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } }   这个类将对某个bean中所有被 @Autowire@Value 注解修饰的属性进行填充。 2.4.3:@Autowire VS @Resource   1:无论使用@Resource 还是 @Resource,都不影响bean的初始化顺序。   2:@Resource修饰的属性 要优先于 @Autowire修饰的属性 进行初始化。 2.5:报错流程1: getBean("brokenComponent") -> getSingleton("brokenComponent") -> beforeSingletonCreation("brokenComponent") -> createBean("brokenComponent") -> addSingletonFactory("brokenComponent") -> inject() -> 2: getBean("tubunComponent",TubunComponent.class) -> getSingleton("tubunComponent") -> beforeSingletonCreation("tubunComponent") -> 3:getBean("twConfig") -> getSingleton("twConfig") -> beforeSingletonCreation("twconfig") -> addSingletonFactory("twConfig") -> afterSingletonCreation("twConfig") 4:getBean("denpaComponent") -> getSingleton("denpaComponent") -> beforeSingletonCreation("denpaComponent") -> createBean("denpaComponent") -> addSingletonFactory("denpaComponent") -> inject() -> 5:getBean("tolenComponent") -> getSingleton("tolenComponent") -> beforeSingletonCreation("tolenComponent") -> createBean("tolenComponent") -> addSingletonFactory("tolenComponent") -> inject() -> 6:getBean("tubunComponent",TubunComponent.class) -> getSingleton("tubunComponent") -> beforeSingletonCreation("tubunComponent") -> 报错 7:afterSingletonCreation("tolenComponent") -> afterSingletonCreation("denpaComponent") -> afterSingletonCreation("tubunComponent") -> afterSingletonCreation("brokenComponent")   可以看到 tubunComponent 进行了两次 getSingleton,经过循环依赖检查的时候报错了。 2.6:错误流程分析归纳   1:Spring 容器先加载 brokenComponent 这个 bean。   2:brokenComponent依赖了tubunComponent(@Resource修饰),因此优先填充 tubunComponent,因此去初始化tubunComponent 这个bean,并将tubunComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。   3:tubunComponent依赖了depenComponent,因此去初始化depenComponent 这个bean,并将depenComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。   4:depenComponent依赖了tolenComponent,因此去初始化depenComponent 这个bean,并将tolenComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。   5:tolenComponent依赖了tubunComponent,因此去初始化tubunComponent 这个bean, 然而当将tubunComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中时发现 tubunComponent 已经存在了,   从而判断这几个bean 产生了循环依赖并跑出异常。   beforeSingletonCreation(String beanName) 方法源码: /** * Callback before singleton creation. *

The default implementation register the singleton as currently in creation. * @param beanName the name of the singleton about to be created * @see #isSingletonCurrentlyInCreation */ protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } 2.7:解决方式   1:使用@Lazy 修饰。   2:这其实是一个操作不当造成的问题,因为spring的三级缓存已经解决了set注入导致的循环依赖,而这几个bean并不是全部都是使用构造器注入。   在 brokeComponent 中使用@Autowired修饰了tolenComponent,又用@Resource 修饰了tubunComponent,   导致tubunComponent要优先于tolenComponent优先加载,而tolenComponent 又依赖了tubunComponent,因此报错。   我们只需要让tolenComponent 优先于 tubunComponent 优先加载就可以了。   实验证明:   1:@Resource 修饰 tolenComponent + @Resource 修饰 tubunComponent 的组合不会报错。   2:@Resource 修饰 tolenComponent + @Autowired 修饰 tubunComponent 的组合不会报错。   3:@Autowired 修饰 tolenComponent + @Autowired 修饰 tubunComponent 的组合不会报错。   偏偏使用了会报错的一种组合......


今日电子行业头条高通下调处理器价格台积电3纳米制程量产1消息称高通将于明年下调中端和入门级骁龙处理器价格12月29日消息,据台媒电子时报援引市场消息称,高通公司可能在2023年下调其中端和入门级骁龙手机处理器的价格,包括骁龙400和6看2023T3出行崔大勇加速中国出行行业新能源化进程站在2022岁末,看2023年,企业家作为中国经济的重要力量,如何看待当前的中国经济政策,对未来中国经济有何期待?新京报贝壳财经邀请百余位来自各行各业的企业家,以问卷方式完成对新一首发上市公司思源电气并购超级电容企业无锡烯晶碳能!深集微研究获悉,上市公司思源电气(002028)12月28日晚间公告,公司将使用6。18亿元,收购烯晶碳能电子科技无锡有限公司(简称烯晶碳能)41。1985的股权。收购完成后,公司相约吉林冰湖,共享渔猎文化!磐石第二届千塘冬捕节活动拉开帷幕银装素裹千塘雪,寻鱼纳福仙人湖。12月28日上午,伴随着渔民一声洪亮的吆喝起鱼了,吉林冬捕经济带启动吉林市分会场暨磐石第二届千塘冬捕节活动在磐石仙人湖拉开帷幕。据悉,本次活动以相约生活是最美的散文诗我的生活也是头条生活是散文诗还是历史剧其实是自己的选择,2022年应该会是载入史册的一年吧?经过三年疫情,从最初的严防死守到彻底放开是三年的等待也是一瞬间的蒙圈,面对突如其来的变化预算8K怎样才能装一台万元电脑?除了海鲜市场,找朋友装机也可以给老友装机,挣钱肯定多少显得俗气,主动倒贴才是最明智的选择。前阵子,已经回老家发展的老友找到我,希望帮忙装台电脑,预算控制在8K以内,能2K爽玩主流游戏大作即可。原来,老友经常在养市场上的键盘太多了,不知道怎么选择?需求最近因为疫情的原因,在家里办公吧,键盘感觉不太行,想要换个键盘,求推荐一般来说,如果是在多人一起办公的情况之下,是建议选择安静一点的键盘的,机械键盘多可以选择静音轴类的,或者是纠结的宝马高价卖不出去,想降价走量却又舍不得品牌对于宝马的经销商来说,最近似乎又陷入了新的纠结。一方面宝马i3上个月交付了3394辆,这个成绩虽然无法与特斯拉比亚迪们相提并论。但对比宝马自己的新能源序列,已经配得上畅销二字。但另苹果遮羞布被扯下,人民网点赞国产手机品牌,这才是良心品牌在11月份iPhone销量表现不佳,可以明显感受到苹果手机口碑正在下滑,对比安卓手机优势正在下降,选择苹果或安卓体验相差没有之前那么大。今年发布的iPhone14加入了灵动岛,热度2023年最佳加密市场最佳选择狗狗币(DOGE)?概括鉴于埃隆马斯克(ElonMusk)对meme硬币的喜爱,DOGE多头希望与Twitter集成以进行应用内支付。鉴于公司最近采取的措施,我认为这不太可能。闪电消费已停止。Twit中国半导体必将崛起,光刻机技术研究已开启,芯片卡脖子有望突破华为真是不鸣则已,一鸣惊人啊。自从被老美制裁之后,就一直忍气吞声,结果这一出手就是王炸,你知道吗?华为已经在造光刻机了。这是国家知识产权总局前段时间公布的专利申请信息。看不懂没关系
乔伊斯我会残暴KO乌西克乔书亚!泰森富里不敢打就放弃金腰带文搏击江湖WBC重量级银腰带得主和WBO国际重量级冠军英国人乔乔伊斯(140,13KO)上周4回合残暴KO哈默之后,信心爆棚。他扬言有能力残暴KO现WBAIBF和WBO世界重量级冠津门虎班底厚度不足开始显现与国安相比欠缺经验北京时间7月4日鲍道,天津津门虎在昨晚1比2不敌北京国安之后,中超三连胜也便戛然而止,本场比赛球队板凳厚度不足的短板开始显现,当主力球员出现体能下降之后缺少第二梯队的火力输出,而与锡安威廉姆森与鹈鹕队达成2。31亿美元的巨额合同据ShamsCharania周五早上报道,这位伤病缠身的大个子正在与鹈鹕队签订一份为期五年的新秀顶薪合同。这笔交易将支付威廉姆森高达2。31亿美元,并使威廉姆森在202728赛季保4人7。22亿美元!美媒盘点NBA盲盒天才,锡安格里芬双帝领衔今夏顶薪续约合同层出不穷,美媒趁此晒出了一组在新秀期内遭遇赛季报销仍拿到了顶薪续约合同的高顺位球员,锡安格里芬双帝领衔。2019年选秀大会上,鹈鹕用状元签摘下了锡安威廉森,因为体重居家新生活近三成公司转居家办公云办公带火哪些行业?目前,居家办公逐渐成为人们工作的新常态。调查问卷数据显示,已有三成公司由线下办公转变成线上办公模式。北京上海等地实行居家办公措施之后,在小红书上搜索居家办公词条,相关笔记数量超76转会汇总4500万镑!阿森纳官宣签下热苏斯夏季转会窗现已开启,带大家一同跟进足坛转会最新动态!7月4日的足球转会摘要不断更新最新官方消息阿森纳以4500万英镑的转会费签下曼城前锋热苏斯罗马诺德容的第一选择一直是留在巴塞罗那建议中年人如果不差钱,这2种茶能不喝就不喝,难喝不说还伤身茶是我们的国饮,至今已经有数千年的历史,尤其古代的文人墨客喜欢以茶代酒,吟诗作赋。历史上,对茶最痴迷的人恐怕非茶圣陆羽莫属,除此之外,还有一位则是被称之为茶痴的唐代著名诗人卢仝。古不用农药化肥种出来的糙米,吃着才安心一年四季,我们的餐桌上的蔬菜水果会随着四季的变化而有所不同,但对于中国胃来说,米饭是一辈子不变的主角。菜是配着主食吃的,无论多么精美的菜肴,最后都是为了将饭吃下。在众多谷物中,非常瓜帅酝酿战术革命!5000万镑买约克郡皮尔洛一绝招合适曼城当37岁的费尔南迪尼奥离开曼城,瓜迪奥拉决定花钱在后腰位置找一个替补。于是一笔交易出现了4200万英镑800万浮动(其中有500万可能来自18岁中场达科加比)买下英格兰国脚菲利普斯一夜7大市场动态!杜欧交易谈判进展缓慢伊巴卡一年合同续约雄鹿北京时间7月5日,自由球员市场上没有太大的动作。杜兰特和欧文的交易谈判进展迟缓,名记表示篮网有的是时间,他们不会急于达成两人的交易协议。伊巴卡和雄鹿续约一年,独行侠有意塞克斯顿。杜官宣!篮网又完成一笔签约近日篮网官方宣布球队正式与落选秀阿隆德斯威廉姆斯签约。虽说阿隆德斯威廉姆斯在NCAA是一个狠角色,身手全面,是202122赛季ACC联盟最佳球员,场均能得到18。5分6。4篮板5。