1.spring的循环依赖简介
什么是spring的循环依赖?
spring容器的bean互相依赖形成闭环,称为spring的循环依赖。
spring循环依赖只能用于set方法和成员变量注入实现,且要求是单例bean,多例(原型bean)是不能实现循环依赖的。
构造方法注入是不能实现循环依赖的,会抛出异常(Requested bean is currently in creation: Is there an unresolvable circular reference?)
@service
class UserServiceImpl implments UserService{
@Autowried
private EmailService emailService;
}
@service
class EmailServiceImpl implments EmailService{
@Autowried
private UserService userService;
}
为什么Spring解决循环依赖比较麻烦呢?因为Spring创建一个Bean是需要通过反射来构建的,构建过程中无法感知这个类具体是什么类型的,它只能够实例化一个填充一个实体!于是:
创建 UserServiceImpl完成后发现依赖EmailServiceImpl !
于是创建EmailServiceImpl ,但是创建完成后又发现依赖于UserServiceImpl!
于是又去创建UserServiceImpl,又发现EmailServiceImpl !
然后我又去创建EmailServiceImpl
…
2.如何解决循环依赖?
实例化:调用构造函数将对象创建出来
初始化:调用构造函数将对象创建出来后,给对象的属性也被赋值
可以创建两个容器(Map),一个起名为singletonObjects,一个起名为earlySingletonObjects!
「singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!
「earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!
我们有了这两个Map对象,再次试图创建一个被循环依赖的bean!
创建 UserServiceImpl完成后,把自己存到「earlySingletonObjects」里面去,然后发现依赖EmailServiceImpl !
于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找发现也没有,开始新建!
创建EmailServiceImpl 完成后,把自己存放到「earlySingletonObjects」里面去,然后发现依赖UserServiceImpl!
于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找,发现了UserServiceImpl对象!
将「earlySingletonObjects」返回的对象UserServiceImpl设置到EmailServiceImpl 中去,创建完成!
把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!
UserServiceImpl将返回的EmailServiceImpl 设置到对应的属性中,创建完成!
把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!
3.Spring为什么使用三级缓存解决呢?
通过上面的解释我们大概明白了循环依赖的解决方案,明明采用二级缓存就能够解决循环依赖,但是Spring为什么使用了三级缓存呢?
我们先来了解一下Spring每个缓存的名字及其作用:
「singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!
「earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!
「singletonFactories」:提前暴露的对象,存放已经创建完成,但是还没有注入好的对象的工厂对象!通过这个工厂可以返回这个对象!
为什么明明使用二级缓存就能够解决的问题,spring偏偏要使用三级缓存去解决呢?
上面的设计方案二级缓存是能够很好的解决循环依赖所带来的问题,但是请大家思考一个问题:
我们创建的bean所依赖的对象是一个需要被Aop代理的对象,怎么办?遇到这种情况,我们肯定不能够直接把创建完成的对象放到缓存中去的!为什么,因为我们期望的注入的是一个被代理后的对象,而不是一个原始对象! 所以这里并不能够直接将一个原始对象放置到缓存中,我们可以直接进行判断,如果需要Aop的话进行代理之后放入缓存!
但是Aop的操作是在哪里做的?是在Spring声明周期的最后一步来做的!如果我们进行判断创建的话,Aop的代理逻辑就会在创建实例的时候就进行Aop的代理了,这明显是不符合Spring对于Bean生命周期的定义的! 所以,Spring重新定义了一个缓存【「singletonFactories」】用来存放一个Bean的工厂对象,创建的对象之后,填充属性之前会把创建好的对象放置到【「singletonFactories」】缓存中去,并不进行实例化,只有在发生了循环引用,或者有对象依赖他的时候,才会调用工厂方法返回一个代理对象,从而保证了Spring对于Bean生命周期的定义!
以下是spring关于三级缓存的定义:
主要原理是利用三级缓存机制:
Map<String, Object> singletonObjects: 一级缓存,也就是我们平常理解的单例池,存放已经完整经历了完整生命周期的bean对象。
Map<String, Object> earlySingletonObjects: 二级缓存,存储早期暴露出来的bean对象,bean的生命周期未结束。(属性还未填充完)
Map<String,ObjectFactory<?> > singletonFactories: 三级缓存,存储生成bean的工厂。
注意:只有单例bean会通过三级缓存提前暴露出来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例bean是没有缓存的,不会将期放到三级缓存中。
4.spring三级缓存的大致流程
A在创建过程中需要B,于是A先将自己放到三级缓存里面,去实例化B
B实例化的时候发现需要A,于是B先查一级缓存,没有再查二级缓存,还是没有,再查三级缓存,找到了A;然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态);然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放入到一级缓存中
版权归原作者 小唐编程 所有, 如有侵权,请联系我们删除。