什么是循坏依赖?
出现循坏依赖的场景:
比如AService里面依赖着BService,在A中调用B
在BService中又依赖着AService
A依赖着B,B依赖着A所以就产生了循坏依赖
SpringBean的创建生命周期
介绍下三级缓存
单例池:完成所有Bean创建步骤之后会加入到这里,存储完整代理对象
二级缓存:early(提早的)加入缓存,用来保存那些还没经过完整生命周期的bean,不完整的bean对象,因为一级是存储加载完的bean。还有就是保证bean的单例状态,不能每次都再创建一个对象,可以提供给后续需要用到这个对象可以直接使用。
三级缓存:存储原生对象。存储的lambd表达式,lambd表达式里面是普通对象
3个缓存底层实际是3个Map
为什么需要用到缓存?
在上面的BService中也依赖了AService,所以在Abean创建的时候填充BService也需要将BServiceBean创建出来,但在实例化B的时候因为也依赖了A所以需要拿到A对象,但是B是在创建A时B注入了所以现在加载B,到目前A的整个生命周期还没走完,这时候B在单列池中也就找不到A。
单列池是完成所有步骤之后就会加入到单列池,但现在才走到第2步,如果按照正常的逻辑B在单列池中找不到A就再去创建A,再到B还是整个流程走不完,A还是不会到单列池,也就形成了闭环。
怎么解决上述循坏依赖?
只需要在A创建的时候添加到二级缓存Map(因为而已缓存名称就叫early所以就表示专门用来存储早期的Bean,目前就是A还没走完整个步骤,但因为A中注入了B所以得暂停先走完B的Bean创建步骤,而B有依赖了A这时候为了让B拿到A对象,所以A得提早存在缓存中),然后在B中加载A的时候如果在单例池(一级缓存)中没有找到,可以在二级缓存中找。
上面看起来只需要两个map就可以解决循坏依赖,为什么还需要3级缓存?
场景是如果在执行第4步时A需要进行AOP,那么此时就需要产生A的代理对象,但前面通过A的构造方法创建的普通A对象,那么现在应该加入到单列池的应该是代理对象。
但现在B依赖了A,A需要AOP那么现在给B赋值的应该是什么对象?
这里赋值给B的应该是A的代理对象,但上面我们在创建A和B的时候给的是普通A对象,怎样实现使用AOP的代理对象呢
解决在AOP的情况下
在A初始化前将A添加到set集合表示正在创建,然后创建A,填充B,初始化B(这里会跟A一样添加到Set集合表示正在创建B实例,如果所有步骤都执行完成这个set集合里的名称就会被删除掉,其它的步骤也跟A一样),去单例池找A对象(找不到A还没完成生命周期,如果没有找到并且A没有产生循坏依赖就会去先创建A,)去set集合判断是不是存在循坏依赖,如果存在循坏依赖才会去二级缓存找,如果二级缓存也没找到就表示需要执行AOP了,如果要生成AOP A的代理对象,那么需要先拿到A的普通对象,A的普通对象这时候需要在3级缓存中去找,那到A的普通对象之后就可以生成A代理对象了,得到A代理对象将代理对象添加到二级缓存(因为现在拿到的代理对象还是早期的bean对象,
如果这个时候有CService ,C注入了A也是同样执行上面的步骤,但这个时候因为B的bean在创建的时候已经给A对象AOP了,并且将代理对象添加到了二级缓存,所以C在这时可以在二级缓存中找到A的代理对象
在B创建完A的代理对象后添加到二级缓存是为了之后如果有在需要用到A对象的实例可以直接从二级缓存里拿到,不用重复创建。
在源码中添加到3级缓存用的lambda,lambda做的也就是AOP,因为这样方便判断到底需不需要AOP,3级缓存是在创建A对象时就添加了的
在源码中添加加到3级缓存的代码是提前就加入的,判断只要是单列bean,并且存在循坏依赖,那么这个bean对象就会被加入到3级缓存
lambda调用的这个方法内是通过判断需不需要AOP再来执行,如果不需要AOP返回的就是这个Bean的普通对象
最终就是只要我存在循坏依赖我就会从3级缓存里去拿bean的名字,我就会得到一个对象(普通对象)那么我就会把这个对象放到2级缓存里去,并且会把得到的这个对象赋值给这个属性(比如这里填充Aservice,就会赋值给AService属性)
@Lazy注解为什么可以解决循坏依赖
因为lazy注解是通过生成一个代理对象直接提供的,正常的流程是A需要B是需要去找或者创建B的,但使用了Lazy注解spring,会直接给你一个通过Lazy注解产生的B对象,可以直接用就不会出现循坏依赖了。这里只有构造方法spring是没办法创建对象的,一般是通过无参构造方法创建,因为a调用b对象,b根本都还没创建,根本找不到。
为什么在执行到第4步的时候可以判断到底有没有执行过AOP
因为在第4步的时候会调用一个方法,这个方法中判断了有没有执行过AOP,如果之前执行过AOP会将普通对象和beanname存到这个map里,这里判断也是调的那个map我就可以知道到底有没有进行过AOP
总结:bean的创建过程
1.实例化对象
2.填充属性,依赖注入啥的
在这一步内部如果注入了其它对象需要整个再走一遍
3.填充其它属性
4.其它步骤(AOP)
5.加入单列池
为什么用到三级缓存解决循坏依赖简单理解
首先一级是给整个生命周期走完的成熟bean
那么如果这时候出现了循坏依赖,我们需要打破循坏,而一级只能存完整bean,并且因为存在依赖A不会存在一级缓存中,这时候还在创建B对象,为了先完成B的创建,所以这时候就需要用到2级缓存。
还有一个原因就是为了保持单例bean的状态,如果后续还有要使用这个对象的,不需要重复创建。
而3级缓存就是为了出现AOP的场景时使用的(忘了)
在Bean的创建过程中还有两个非常重要的
一个是set集合:在创建A初始化前将A添加到set集合表示正在创建,如果B执行到需要循坏依赖A时就可以知道现在出现了循坏依赖。
还有一个是Map:为了防止重复AOP在第4步的时候会调用一个方法,这个方法中判断了有没有执行过AOP,如果之前执行过AOP会将普通对象和beanname存到这个map里,这里判断也是调的那个map我就可以知道到底有没有进行过AOP