spring循环依赖
Posted 莫名的猪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring循环依赖相关的知识,希望对你有一定的参考价值。
spring循环依赖主要有三种:
单例引用类型循环依赖(属性):允许
构造器的循环依赖:不允许
多例循环依赖:不允许
单例引用类型循环依赖(属性)
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Data
@Component
public class CirculeA
@Autowired
private CirculeB circuleB;//引用CirculeB
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Data
@Component
public class CirculeB
@Autowired
private CirculeA circuleA;//引用CirculeA
@org.junit.Test
public void test3()
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("spring启动成功");
总结:在circuleA实例化过程中触发circuleB的getBean(),此时circileA的实例已放入到三级缓存中,在circuleB的实例化过程中会触发circuleA的genBean(),直接从缓存中拿到circileA的实例,这样会优先将circuleB是实例化完成,并在circuleA触发circuleB的getBean()时返回,然后继续完成circuleA的实例化;
circuleA第一次实例化会走以下代码,第二次直接从缓存中获取不会走以下代码
单例实例化流程图:
构造器的循环依赖:
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component
public class CirculeB
private CirculeA circuleA;
public CirculeB(CirculeA circuleA)
this.circuleA = circuleA;
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component
public class CirculeA
private CirculeB circuleB;
public CirculeA(CirculeB circuleB)
this.circuleB = circuleB;
报错:org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'circuleA' defined in file [D:\\XXX\\5.2.8\\spring_demo\\spring_test\\target\\classes\\com\\spring\\bean\\CirculeA.class]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'circuleB' defined in file [D:\\xxxx\\5.2.8\\spring_demo\\spring_test\\target\\classes\\com\\spring\\bean\\CirculeB.class]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?
//创建实例,在这个方法中触发circuleB的getBean()
if (instanceWrapper == null)
instanceWrapper = createBeanInstance(beanName, mbd, args);
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
//AutowiredAnnotationBeanPostProcessor 收集有@Autowire和@Value注解的方法和属性,
// 放入到injectionMetadataCache缓存中,包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
//CommonAnnotationBeanPostProcessor 收集有@PostConstruct和@PreDestroy注解的方法 放入到lifecycleMetadataCache,
// 有@Resource注解的方法和属性 放入到injectionMetadataCache缓存中,
// 包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
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));
总结:在放入缓存前调用getBean(),导致缓存中没有,所以每次调用getbean()都会走beforeSingletonCreation()方法,在第二次调用时判断以下条件时(!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName))返回true,会抛出异常
多例循环依赖:
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CirculeB
@Autowired
private CirculeA circuleA;
package com.spring.bean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CirculeA
@Autowired
private CirculeB circuleB;
@org.junit.Test
public void test4()
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
CirculeA bean = context.getBean(CirculeA.class);
CirculeB bean1 = context.getBean(CirculeB.class);
System.out.println("spring启动成功");
会报错:org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'circuleA': Unsatisfied dependency expressed through field 'circuleB';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'circuleB': Unsatisfied dependency expressed through field 'circuleA';
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?
原因:第一次调用会在ThreadLocal中存放,在第二次调用以下方法时抛出异常
Spring的循环依赖问题
spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢?首先让我们来定义循环引用类:
在Spring中将循环依赖的处理分成了3种情况:
- 构造器循环依赖
- setter循环依赖
- prototype范围的依赖处理
构造器循环依赖
通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。
如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建TestC,最终在创建TestC时发现又需要TestA,从而形成一个环,没办法创建。
Spring容器将每一个正在创建的bean标识符放在一个"当前创建bean池"中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在"当前创建bean池"里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从"当前创建bean池"中清除掉。
setter循环依赖
通过setter注入方式构成的循环依赖。对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。
- Spring容器创建单例"testA"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testA"标识符放到"当前创建bean池",然后进行setter注入"testB"。
- Spring容器创建单例"testB"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testB"标识符放到"当前创建bean池",然后进行setter注入"testC"。
- Spring容器创建单例"testC"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testC"标识符放到"当前创建bean池",然后进行setter注入"testA"。进行注入"testA"时由于提前暴露了"ObjectFactory"工厂,从而使用它返回提前暴露一个创建中的bean。
- 最后再依赖注入"testB"和"testA",完成setter注入。
prototype范围的依赖处理
对于"prototype"作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。
对于"singleton"作用域bean,可以通过"setAllowCircularReferences(false);"来禁用循环引用。
以上是关于spring循环依赖的主要内容,如果未能解决你的问题,请参考以下文章