文章目录
一、产生原因
前面已经讲过Spring bean的生命周期,我们知道bean在实例化之后,要经过填充属性、初始化完整之后才能放入单例池使用。那么,假如beanA依赖beanB,beanA填充属性的时候就会触发beanB的生命周期。而beanB又依赖beanA就会发现beanA还没有准备好无法注入,就会陷入相互等待的死循环,这就是循环依赖。如图
二、三级缓存
Spring为了解决循环依赖,使用了三级缓存,三级缓存分别对应DefaultSingletonBeanRegistry类定义的三个Map。注意,它们的顺序不是按照一二三定义的
2.1 环境准备
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpring {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Object o = context.getBean("userService");
System.out.println(o);
}
}
AppConfig
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("com.spring.study.service")
public class AppConfig {
}
OrderService
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
@Autowired
private UserService userService;
}
UserService
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserService {
@Autowired
private OrderService orderService;
}
2.2 源码跟踪
了解了bean的生命周期,咱从getBean(“orderService”)——>doGetBean——>getSingleton进入
2.2.1 OrderService获取过程
OrderService先开始
这块已经开始使用三级缓存,一级缓存singletonObjects还没有OrderService的实例,OrderService也还没开始创建,因此第一次不会进入if,之后继续
这块在bean的生命周期讲过,不再赘述。在getSingleton里面会执行这个lamba,进入createBean——>doCreateBean
在这之前bean已经实例化,如果bean是单例的,允许循环依赖,并且正在创建中进入if。这块是最关键的地方,addSingletonFactory用于打破循环
将单例bean的工厂放入三级缓存singletonFactories,以便随时执行getObject()得到实例化的bean。
返回再看看getEarlyBeanReference
如果bean,有被AOP,这里返回的就是AOP之后的代理对象,否则就是原来的对象,AOP后面讲。
目前的bean已经被实例化,尚未初始化。populateBean填充属性,会触发UserService的生命周期getBean(“userService”)
2.2.2 UserService获取过程
UserService的过程与OrderService类似,再次进入populateBean方法,填充属性,这时需要注入一个orderService。进入populateBean–>ibp.postProcessProperties,进入到AutowiredAnnotationBeanPostProcessor处理自动装配
继续进入,这块处理orderService字段
继续进入resolveDependency–>doResolveDependency–>descriptor.resolveCandidate(autowiredBeanName, type, this);
再次回到getBean(“orderService”)
2.2.3 再次OrderService获取过程
再次进入getSingleton,orderService正在创建中,因此进入了if,此时一、二级缓存都为空,只有三级缓存有bean的工厂
调用getObject(),就得到了orderService提前暴露的实例,然后把实例提升到二级缓存,删除三级缓存的值。一路返回,完成了orderService的注入,再完成UserService的初始化放入到一级缓存。继续完成userService注入到OrderService,完成OrderService的初始化。
之后在这里将OrderService实例提升到一级缓存
再次删除二级和三级中的缓存。到目前为止OrderService和UserService的实例都加入了一级缓存,解决了它们之间的循环依赖。这个过程中二级缓存earlySingletonObjects,好像没啥用,userService先加入earlySingletonObjects,然后再加入一级缓存singletonObjects同时在earlySingletonObjects中删除。感觉有点多此一举,下面咱们看下为啥要三级缓存
三、为啥要三级缓存,两级可以吗
singletonObjects 一级缓存
earlySingletonObjects 二级缓存
singletonFactory 三级缓存
3.1 过程梳理
BeanA实例化–>放入三级缓存–>填充属性BeanB,BeanC–>
(触发BeanB实例化–>放入三级缓存–>填充属性BeanA–>BeanA正在创建中,从三级缓存取出–>得到AOP之后的BeanA,放入二级缓存–>进行BeanB的AOP–>完成beanB创建–>BeanB放入一级缓存)–>
(触发BeanC实例化–>放入三级缓存–>填充属性BeanA–>BeanA从二级缓存取出–>得到AOP之后的BeanA–>进行BeanB的AOP–>完成beanB创建–>BeanC放入一级缓存)–>
判断是否需要AOP–>已经提前AOP–>完成benaA的创建–>BeanA放入一级缓存
3.2 分析
- BeanB、BeanC的AOP在填充属性完成之后
- BeanA的AOP在三级缓存中进行,也就是在填充属性完成之前,提前AOP了
- 如果没有二级缓存,BeanC在创建过程中再次从三级缓存获取得到的BeanA将是一个新的AOP对象,因为每次AOP之后的代理对象都是不同的,不可行
- 如果没有三级缓存,实例化之后的BeanA直接放入二级缓存,注入BeanB,BeanC的实例就是未经过AOP的。而BeanA最后会AOP之后放入一级缓存,不可行
- 如果没有三级缓存,实例化之后的BeanA先经过AOP再放入二级缓存,这是可以的。但是,这么做之后所有bean的AOP都提前了。实际Spring是希望只有发生循环依赖的bean才提前进行AOP
- 所以,实例化之后的BeanA,创建一个进行AOP的工厂,将这个工厂放入三级缓存。在发生循环依赖的时候从工厂取出经过AOP的BeanA,再将经过AOP的BeanA放入二级缓存。这样BeanA中的BeanB、BeanC都可以从二级缓存取到同一个AOP之后的BeanA,只有BeanA的AOP提前了。
3.3 结论
- 三级缓存保证了只有发生循环依赖的bean提前AOP
- 二级缓存保证多个属性循环依赖可以取到同一个AOP之后的Bean
- 一级缓存就是最终的单例池,bean放入之后,删掉二、三级缓存的bean。
- 二、三级缓存为了解决循环依赖而存在,如果没有循环依赖,一级缓存就够了。
- 有循环依赖,没有AOP,两级缓存也够了。