Spring的循环依赖问题

直接进入主题,众所周知,Spring是有三级缓存的,DefaultSingletonBeanFactory中的singletonObjects为一级缓存Map<BeanName, Object>,二级缓存为earlySingletonObjects半成品池也是根一级缓存一样的类型,三级缓存为singletonFactories,类型为Map<BeanName, ObjectFactory<?>>。
在这里插入图片描述
spring进行扫描 -> 反射后封装成beanDefinition对象 -> 放入beanDefinitionMap -> 遍历map -> 验证(是否单例、是否延迟加载、是否抽象) -> 推断构造方法 -> 准备开始进行实例 -> 去单例池中查,没有 -> 去二级缓存中找,没有提前暴露 -> 生成一个objectFactory对象暴露到三级缓存中 -> 属性注入,发现依赖Y -> 此时Y开始它的生命周期直到属性注入,发现依赖X -> X又走一遍生命周期,当走到去三级缓存中找的时候找到了 -> 往Y中注入X的objectFactory对象 -> 完成循环依赖。
1、为什么要使用X的objectFacory对象而不是直接使用X对象?
利于拓展,程序员可以通过beanPostProcess接口操作objectFactory对象生成自己想要的对象
2、是不是只能支持单例(scope=singleton)而不支持原型(scope=prototype)?
是。因为单例是spring在启动时进行bean加载放入单例池中,在依赖的bean开始生命周期后,可以直接从二级缓存中取到它所依赖的bean的objectFactory对象从而结束循环依赖。而原型只有在用到时才会走生命周期流程,但是原型不存在一个已经实例化好的bean,所以会无限的创建->依赖->创建->依赖->…。

首先spring容器启动,然后加载所有的Spring管理的类把他变成BeanDefinition放入到一个BeanDefinitionMap中,然后对这个BeanDefinitionMap做一个遍历,校验每一个BeanDefinition是否单例是否原型是否懒加载,Bean名字是否符合,是否抽象等等,校验完之后Spring容器就会接着往下执行,看当时有没有实例化这个类,有没有在单例池也就是一级缓存当中,没有的话就去创建,先推断出一个最佳的构造方法,通过反射来实例化出Java对象,默认情况下Spring会将这个对象的ObjectFactory缓存到三级缓存内的,接着对这个对象做一些初始化的工作,也就是属性填充,接口AbstractBeanFactory的实现类AbstractAutowiredCapableBeanFactory中的populateBean方法来填充Bean,然后执行populate方法中的依赖注入时,会将bean从三级缓存,放到二级缓存,填充完毕后就会执行一些借口的回调比如说各种aware接口、initializationBean接口、@PostContract方法,如果这个对象有AOP的话就会在initializationBean方法中的一个后置回调生成Bean的代理对象,然后放入二级缓存中并删除三级缓存,待初始化完成,在返回到单例池一级缓存中并删除二级缓存,

”Spring是如何解决的循环依赖?“

答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“

答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

首先在AbstractBeanFactory中的doGetBean执行,先getSingleton(BeanName)通过名字去一级缓存和二级缓存中取,发现没有,就下去创建对象,判断是否是单例,是的话就调用getSingleton(BeanName, Factory),通过singletonFactory中的getObject去获取对象,来到AbstractAutowireCapableBeanFactory中的createBean,在这里面有一个doCreateBean方法,
在这里插入图片描述
这里就是反射实例化bean的地方,
在这里插入图片描述
填充Bean在CreateBean中的populateBean中,是相关的填充属性的方法,在initializedBean中会有一个前置、后置处理器,后置处理器是用来包装AOP的也就是通过ObjectFactory来进行包装,并返回到二级缓存中
在这里插入图片描述


最近在使用springBoot高版本做项目的时候,启动时出了循环依赖的报错,表明一下

Description:

The dependencies of some of the beans in the application context form a cycle:

   attrGroupController (field private com.xiang.mall.product.service.CategoryService com.xiang.mall.product.controller.AttrGroupController.categoryService)
      ↓
   categoryService (field private com.xiang.mall.product.service.CategoryBrandRelationService com.xiang.mall.product.service.impl.CategoryServiceImpl.categoryBrandRelationService)
┌─────┐
|  categoryBrandRelationService (field com.xiang.mall.product.service.BrandService com.xiang.mall.product.service.impl.CategoryBrandRelationServiceImpl.brandService)
↑     ↓
|  brandService (field com.xiang.mall.product.service.CategoryBrandRelationService com.xiang.mall.product.service.impl.BrandServiceImpl.categoryBrandRelationService)
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

action提示的很明显了,只需要在配置文件中把循环依赖的开关打开就行了,以前SPringBoot低版本的时候是不需要开启的,应该是默认配置了,在高版本中新加的参数,真好,以后面试又有一个小知识点可以吹了~~~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值