Spring 循环依赖
Spring 面试 About 7,564 words原因
ClassA
的构造中需要ClassB
,ClassB
的构造中需要ClassA
。
public class ClassA {
public ClassA(ClassB classB) {
System.out.println("class a constructor");
}
}
public class ClassB {
public ClassB(ClassA classA) {
System.out.println("class b constructor");
}
}
Spring 官网描述
Circular dependencies If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).
Spring
官网构造循环依赖无法解决,推荐使用setter
方法。但多例情况下也会抛出异常(默认Spring
的Bean
是单例的)。
异常信息
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:219)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 28 more
Spring 解决方案
三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}
singletonObjects
一级缓存,存放经历了完整生命周期的Bean
对象。
earlySingletonObjects
二级缓存,存放实例化完成但初始化还没完成的Bean
对象(属性未初始化)。
singletonFactories
三级缓存,存放ObjectFactory
接口类型的对象(匿名内部类)。实例化完成的Bean
也作为参数传入了该接口内的方法中。
获取单例步骤
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation 判断是否正在创建中,可能在填充属性阶段有其他依赖需要注入
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;
}
Bean 生命周期
Bean
创建经历的生命周期
doGetBean
先从getSingleton(beanName, true)
三级缓存中获取实例,没有则进入getSingleton(String beanName, ObjectFactory<?> singletonFactory)
步骤。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 转换别名等操作获取 beanName
String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 三级缓存中获取实例
bean = sharedInstance; // 省略了 FactoryBean 判断逻辑,假设 Bean 没有实现 FactoryBean
} else {
// 获取 Bean 定义信息
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 如果是单例就创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
// getSingleton 中调用
return createBean(beanName, mbd, args);
});
bean = sharedInstance; // 省略了 FactoryBean 判断逻辑,假设 Bean 没有实现 FactoryBean
}
}
return (T)bean;
}
}
getSingleton
先从三级缓存中获取实例,没有则进入singletonFactory.getObject()
调用的createBean
方法,实际进入doCreateBean
步骤。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 添加标志位表示正在创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
// 获得到实例,调用的是 doGetBean 传入的 createBean 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
} finally {
// 清除标志位
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到一级缓存中,并删除二级、三级缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
}
doCreateBean
createBeanInstance
首先实例化Bean
对象。- 再调用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
加入到第三级缓存中。 populateBean
填充成员变量等属性,成员变量包含了未创建的对象则递归创建Bean
对象。如果依赖了未完整初始化的对象,则将未完整出初始化的对象从三级缓存中加入到二级缓存initializeBean
回调Bean
实现的Aware
接口和后置处理器
addSigleton
getSingleton
中如果Bean
对象创建成功,则newSingleton
为true
,进入addSigleton
,将对象添加到一级缓存中,并删除二级、三级缓存。
示例图
示例图链接:https://www.processon.com/view/link/5fd19adee401fd06ddb82c01
备注
Spring Boot 2.6.0
默认开闭了循环依赖,Spring
官方不提倡使用循环依赖注入方式,希望开发者修改代码。
通过配置可开启:
spring.main.allow-circular-references=true
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓