Spirng 循环依赖报错:Requested bean is currently in creation: Is there an unresolvable circular reference?
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?
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信息,这一步做完才是一个完整的可用的bean |
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance.(一级缓存,存放已初始化完毕的bean) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory.(三级缓存,存放未进行过初始化的beanFactory) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. (二级缓存,存放已实例化,但未进行装配过属性的bean)*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order.(存放已注册了的单例集合 = singletonObjects.size + earlySingletonObjects)*/
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/** Names of beans that are currently in creation.(存放正在被创建的实例,用于检查是否有循环依赖) */
private final Set<String> 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
/**
* ...
*<p>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.
*
* <p>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.
*
* <p>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.
* <p>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 的组合不会报错。
偏偏使用了会报错的一种组合......
Spirng 循环依赖报错:Requested bean is currently in creation: Is there an unresolvable circular reference?的更多相关文章
- SpringBoot项目意外出现 循环依赖和注入的对象意外是Null的问题 Requested bean is currently in creation: Is there an unresolvable circular reference? 或 nested exception is java.lang.NullPointerException
1.Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ...
- Requested bean is currently in creation: Is there an unresolvable circular reference?
spring容器初始化报错:循环依赖,错误信息如下: Requested bean is currently in creation: Is there an unresolvable circula ...
- springboot 异常: Requested bean is currently in creation: Is there an unresolvable circular reference?
2018-07-31 11:56:18.812 WARN 10316 --- [ main] ConfigServletWebServerApplicationContext : Exception ...
- Spring中解决循环依赖报错的问题
什么是循环依赖 当一个ClassA依赖于ClassB,然后ClassB又反过来依赖ClassA,这就形成了一个循环依赖: ClassA -> ClassB -> ClassA 原创声明 本 ...
- Spring:解决因@Async引起的循环依赖报错
最近项目中使用@Async注解在方法上引起了循环依赖报错: org.springframework.beans.factory.BeanCurrentlyInCreationException: Er ...
- IDEA循环依赖报错解决方案
step1.查找循环依赖 step2.在IDEA菜单栏中打开Analyze->Analyze Module Dependencies...看到有的模块被红色的标出来了,此时右边显示了循环依赖,那 ...
- spring jpa 实体互相引用返回restful数据循环引用报错的问题
spring jpa 实体互相引用返回restful数据循环引用报错的问题 Java实体里两个对象有关联关系,互相引用,比如,在一对多的关联关系里 Problem对象,引用了标签列表ProblemLa ...
- dubbo-admin-2.5.3 运行报错: Bean property 'URIType' is not writable or has an invalid 解决方法
因为 jdk 是1.8的版本,和 dubbo-admin 存在兼容性问题.所以报错: Bean property 'URIType' is not writable or has an invalid ...
- yum 安装 依赖报错
今天使用yum安装的时候 报错: Error: Multilib version problems found. This often means that the root cause 应该是yum ...
随机推荐
- Java 复习整理day07
package com.it.demo05_innerclass; /* 案例: 演示内部类入门. 概述: 所谓的内部类指的是类里边还有一个类, 里边那个类叫: 内部类, 外边那个类, 叫外部类. 分 ...
- 搜索Ex (洛谷提高历练地)
搜索Ex P1120 小木棍 题意:不超过65根长度小于50的小木棍(是由一批等长木棍砍断得到的),求原始木棍的最小可能长度 分析:优化+减枝爆搜 搜索状态要记录当前尝试的已经填好的长度,当前尝试填的 ...
- Codeforces Round #646 (Div. 2) B. Subsequence Hate(前缀和)
题目链接:https://codeforces.com/contest/1363/problem/B 题意 可以将 $01$ 串中的 $0$ 变为 $1$.$1$ 变为 $0$,问至少需要变换多少字符 ...
- python+selenium+bs4爬取百度文库内文字 && selenium 元素可以定位到,但是无法点击问题 && pycharm多行缩进、左移
先说一下可能用到的一些python知识 一.python中使用的是unicode编码, 而日常文本使用各类编码如:gbk utf-8 等等所以使用python进行文字读写操作时候经常会出现各种错误, ...
- Codeforces Round #641 (Div. 2) D. Orac and Medians (贪心)
题意:有一个长度为\(n\)的数组,问能否通过多次使某个区间的所有元素变成这个区间的中位数,来使整个数组变成题目所给定的\(k\). 题解:首先这个\(k\)一定要在数组中存在,然后我们对中位数进行考 ...
- 004、Python xlsxwriter模块
简单用法demo # !/usr/bin/python # coding:utf-8 # xlsxwriter的基本用法 import xlsxwriter # 1. 创建一个Excel文件 work ...
- 找工作面试题记录与参考资料(Golang/C++/计算机网络/操作系统/算法等)
记录下去年(2020年)找工作的面试题及参考资料. C++ 智能指针的实现原理 多态的实现原理[2] C++11/14/17新特性[3] 手写memcpy和memmove[4] 介绍下boost库 计 ...
- ssh原理及加密传输
1.ssh??(保证过程中是加密的,即安全的)ssh 是 Secure Shell 的缩写,是一个建立在应用层上的安全远程管理协议.ssh 是目前较为可靠的传输协议,专为远程登录会话和其他网络服务提供 ...
- 在kubernetes集群里集成Apollo配置中心(1)之交付Apollo-adminservice至Kubernetes集群
1.部署apollo-adminservice软件包 apollo-adminservice软件包链接地址:https://github.com/ctripcorp/apollo/releases/d ...
- MySQL 连接管理
目录 MySQL 连接方式 TCP/IP 连接 Socket 连接 MySQL 连接工具 自带连接工具 第三方连接工具 MySQL 连接方式 TCP/IP 连接 # TCP/IP 连接 mysql - ...