循环依赖

所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B(A依赖B), B->A(B依赖A) 。在Spring中,如果A->B,那么在创建A的过程中会去创建B,在创建B(或B的依赖)的过程中又发现B->A,这个时候就出现了循环依赖的现象。
循环依赖的解决
spring中的循环依赖只有当
1.Bean是单例,
2.通过属性注入的情况
这两个条件满足的情况下是没问题的。但是如果是通过构造器依赖,或者不是单例模式的情况下循环依赖就会抛出异常BeanCurrentlyInCreationException。下面从代码层面上解析一下为什么。
Prototype的循环依赖问题
为什么最先介绍Prototype的循环依赖呢,因为可以顺便介绍在Spring中创建Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。这个方法很长,这里只写出核心逻辑,并在注解上注明了个人理解:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
//尝试获取单例对象,因为spring大部分的bean都是单例的,所以这里先尝试能否获取。
registered singletons.
Object sharedInstance = getSingleton(beanName);
//单例存在的情况下,那么beanName返回的肯定是单例类,但是这里还需要判断是不是FactoryBean
if (sharedInstance != null && args == null) {
...
//FactoryBean应该返回getObject()对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//走到这里,有可能beanName是单例模式,但之前并没有实例化,或者是Prototype类型。
//首先判断不是循环依赖,这里的循环依赖指的是Prototype类型
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 如果是单例,则创建单例模式
if (mbd.isSingleton()) {
// !!!这里是解决单例循环依赖的关键,后面再分析
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 原型模式,则创建一个新对象.
Object prototypeInstance = null;
try {
/*这里是Prototype循环依赖的问题,会记录在map中beanName,
如果在解决当前Bean的依赖过程中还依赖当前Bean,
则说明了出现了循环依赖
*/
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//对应beforePrototypeCreation(),从map中移除
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
...
}
}
...
return (T) bean;
}
可以看出,该流程中就考虑了Prototype的循环依赖的问题,只要在创建Prototype的Bean中出现循环依赖那么就抛出异常。但是在singleton的情况下,则通过另外的方式来解决。
Singleton的循环依赖之构造注入
在上面介绍中,出现了一个很关键的地方:
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
throw ex;
}
}
});
这个getSingleton涉及到了ObjectFactory这个接口类,这个接口的功能和FactoryBean类似,但是主要是用来解决循环依赖的。在初始化过程同决定返回的Singleton对象是。关于单例的对象的创建,又要介绍一下DefaultSingletonBeanRegistry这个类,这个类主要用来帮助创建单例模式,其中主要的属性:
/** 缓存创建的单例对象: bean名字 --> bean对象 */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** 缓存单例的factory,就是ObjectFactory这个东西,: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); /** 也是缓存创建的单例对象,功能和singletonObjects不一样, 在bean构造成功之后,属性初始化之前会把对象放入到这里, 主要是用于解决属性注入的循环引用: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); /** 记录在创建单例对象中循环依赖的问题,还记得Prototype中又记录创建过程中依赖的map吗? 在Prototype中只要出现了循环依赖就抛出异常,而在单例中会尝试解决 */ private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
现在回过来看getSingleton(beanName, new ObjectFactory<Object>()这个方法的实现。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
//尝试在singletonObjects中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//不存在则创建
//把当前beanName加入到singletonsCurrentlyInCreation中
beforeSingletonCreation(beanName);
try {
singletonObject = singletonFactory.getObject();
}
...
finally {
...
//从singletonsCurrentlyInCreation中删除beanName
afterSingletonCreation(beanName);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
这段逻辑是不是和Prototype中解决循环类似,这里其实就是调用了ObjectFactory的getObject()获取对象,回过头去看前面代码,ObjectFactory的getObject()方法实际调用的是createBean(beanName, mbd, args)。说到createBean(beanName, mbd, args)又不得不说AbstractAutowireCapableBeanFactory这个类,主要功能就是完成依赖注入的Bean的创建,这个类的createBean方法代码如下,注意注解说明:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
...
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 实例化bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//如果没实例化则创建新的BeanWrapper
//如果是通过构造器注入,这里是一个关键点
/*
因为在A初始化的时候发现构造函数依赖B,就会去实例化B,
然后B也会运行到这段逻辑,构造函数中发现依赖A,
这个时候就会抛出循环依赖的异常
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//如果当前是单例,并且allowCircularReferences为true(默认就是true,除非我们不希望Spring帮我们解决)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
/*
!!!这里很重要,把构造成功,但属性还没注入的
的bean加到singletonFactory中,这样再解决A的依赖
过程中如果依赖A,就把这个半成品返回回去。
*/
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
Object exposedObject = bean;
try {
//自动注入属性
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
...
return exposedObject;
}
注解已经注明了我的理解。就不再赘述
总结
上面代码是我一边debug一个写下的,现在写完了,根据自己的理解总结一下。
相关类说明
AbstractBeanFactory,这个类中包含了Bean创建的主要流程,在doGetBean这个方法中包含了对Prototype循环依赖处理。逻辑很简单,出现了循环依赖则直接抛出异常
DefaultSingletonBeanRegister 用于管理Singleton的对象的创建,以及解决循环依赖的问题,其中解决循环依赖的关键属性就是了earlySingletonObjects,他会在构造Singleton对象过程中暂时缓存构造成功,但属性还未注入的对象,这样就可以解决循环依赖的问题。
AbstractAutowireCapableBeanFactory,自动注入的相关逻辑,包自动注入的对象的创建、初始化和注入。但如果在调用构造函数中发现了循环依赖,则抛出异常
ObjectFactory,这个接口功能和FactoryBean类似,但是为了解决循环依赖,他决定了在获取的getSingleton()是一个完成品还是一个半成品。
思考
如果A--构造依赖->B,B--属性依赖-->A,例如:
@Component
public class BeanA {
private BeanB beanB;
@Autowired
public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
@Autowired
private BeanA beanA;
}
这种情况会异常吗?提示:都有可能
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# spring循环依赖策略
# spring循环
# spring依赖策略
# Spring循环依赖正确性及Bean注入的顺序关系详解
# 浅谈Spring解决循环依赖的三种方式
# 浅谈Spring 解决循环依赖必须要三级缓存吗
# Spring循环依赖的三种方式(推荐)
# 详解Spring循环依赖的解决方案
# 深入理解Spring中的循环依赖
# 浅谈Spring如何解决循环依赖的问题
# 浅入浅出的讲解Spring循环依赖问题
# 抛出
# 过程中
# 出现了
# 是一个
# 情况下
# 就会
# 这段
# 这种情况
# 这个时候
# 会去
# 中又
# 自己的
# 的是
# 都是
# 都有
# 还没
# 闭环
# 也会
# 包含了
# 多个
相关文章:
微信小程序制作网站有哪些,微信小程序需要做网站吗?
济南企业网站制作公司,济南社保单位网上缴费步骤?
清单制作人网站有哪些,近日“兴风作浪的姑奶奶”引起很多人的关注这是什么事情?
网站好制作吗知乎,网站开发好学吗?有什么技巧?
如何在Golang中使用replace替换模块_指定本地或远程路径
长沙企业网站制作哪家好,长沙水业集团官方网站?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
如何使用Golang安装API文档生成工具_快速生成接口文档
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
PHP 500报错的快速解决方法
如何用5美元大硬盘VPS安全高效搭建个人网站?
整人网站在线制作软件,整蛊网站退不出去必须要打我是白痴才能出去?
如何在云服务器上快速搭建个人网站?
建站之星代理如何获取技术支持?
宝塔面板创建网站无法访问?如何快速排查修复?
如何快速生成专业多端适配建站电话?
香港服务器WordPress建站指南:SEO优化与高效部署策略
如何选择可靠的免备案建站服务器?
如何用狗爹虚拟主机快速搭建网站?
如何配置支付宝与微信支付功能?
如何将凡科建站内容保存为本地文件?
如何在香港免费服务器上快速搭建网站?
如何通过商城免费建站系统源码自定义网站主题?
如何高效配置IIS服务器搭建网站?
如何用低价快速搭建高质量网站?
高端企业智能建站程序:SEO优化与响应式模板定制开发
php8.4新语法match怎么用_php8.4match表达式替代switch【方法】
专业公司网站制作公司,用什么语言做企业网站比较好?
如何通过.red域名打造高辨识度品牌网站?
西安大型网站制作公司,西安招聘网站最好的是哪个?
如何通过西部建站助手安装IIS服务器?
如何在Golang中使用encoding/gob序列化对象_存储和传输数据
网站制作专业公司有哪些,如何制作一个企业网站,建设网站的基本步骤有哪些?
高性能网站服务器配置指南:安全稳定与高效建站核心方案
个人摄影网站制作流程,摄影爱好者都去什么网站?
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
宝塔建站后网页无法访问如何解决?
开心动漫网站制作软件下载,十分开心动画为何停播?
如何快速搭建个人网站并优化SEO?
怀化网站制作公司,怀化新生儿上户网上办理流程?
贸易公司网站制作流程,出口贸易网站设计怎么做?
常州企业建站如何选择最佳模板?
建站DNS解析失败?如何正确配置域名服务器?
如何通过VPS建站无需域名直接访问?
企业宣传片制作网站有哪些,传媒公司怎么找企业宣传片项目?
html制作网站的步骤有哪些,iapp如何添加网页?
广州网站制作的公司,现在专门做网站的公司有没有哪几家是比较好的,性价比高,模板也多的?
金*站制作公司有哪些,金华教育集团官网?
建站之星体验版:智能建站系统+响应式设计,多端适配快速建站
如何通过虚拟主机快速完成网站搭建?
*请认真填写需求信息,我们会在24小时内与您取得联系。