spring循环依赖&三级缓存
三级缓存
想来大家面试的时候,都被问到过三级缓存以及循环依赖,那么三级缓存到底是什么,又解决了什么问题呢
下面来看看三级缓存
三级缓存解释
singletonObjects : 存放最终生成的单例对象
earlySingletonObjects : 存放早期的对象,也就是A、B循环依赖的时候,先生成的那个不完成的bean
singletonFactories : 存放FactoryBean生成的对象
观点
其实三级缓存也好,二级或者一级缓存也好,都能解决循环依赖的问题,也能解决spring容器关于存放生成的实例bean的问题
因为spring有存beanDefinition,而在beanDefinition中对于bean有详细的描述,所以一级缓存也行
但是,三级缓存逻辑更清晰,不管是开发spring的也好,读源码的也好,都不会有太多的混乱
循环依赖
有以下两个程序,可以明显看到是循环依赖的代码
HelloWorld.java
@Slf4j
@Component
public class HelloWorld {
@Autowired
private Person person;
public void sayHello(){
log.info("say hello");
}
}
Person.java
@Slf4j
@Component
public class Person {
@Autowired
private HelloWorld helloWorld;
public void eat(){
log.info("eat");
}
}
解决循环依赖
那么,spring容器又是怎么去解决循环依赖的问题呢,看下图
- 要填充Person的过程中,发现有个属性是Helloworld,需要实例化Helloworld
- 在填充Helloworld过程中,发现有个属性是Person,但是Person已经实例化好了,就把Person放到earlySingletonObjects中
- 取Person对象时,发现singletonObjects中没有,于是就去earlySingletonObjects中找
- 找到Person对象后,把Person对象填充到Helloworld对象中,并在earlySingletonObjects中删除Person对象
- 把Helloworld对象放到singletonObjects,然后填充Person对象中的Helloworld
- 把Person对象放到singletonObjects中
总结一下:
循环依赖问题是由于A、B类相互引用,然后在填充对象的过程中,singletonObjects找不到对象,就会一直去创建,所以才会死循环
解决死循环的方法就是,把A类或者B类没有填充属性的对象暂存起来,那么在填充对象时找到相应的对象,就会跳出循环
关于循环依赖的一些看法
- 造成循环依赖的究极原因是循环调用,在一些中间件中,代码写的也不是那么高大上
- 特别是国外的一些程序员,最喜欢用递归,比如mybtis中filter的调用,tomcat中filter的执行,都存在递归调用
- 递归有递归的好处,递归存在代码量少的优点,但是非常容易出问题
- 关于循环依赖,在我看来还有另外一种设计
1) spring在扫描类之后,需要生成的单例bean的数据已经确定,产生循环依赖的问题实在填充属性的时候,
2) 使用for循环进行单例bean的生成,只不过是没有填充属性
3) 使用for循环进行属性填充,效率高,不容易产生问题 - 有可能springIOC的作者在写第一版的时候,就用了递归,但是后来遇循环依赖问题的时候,只是对问题进行修改,没有对代码进行重构,所以才有了我们今天看到的代码