什么是循环依赖?
简单来说就是A、B两个对象互相依赖,A中有B,B中有A。
循环依赖分类:
- 构造器循环依赖
//A类中引用B类实例
class A {
B b;
public A(B b){
this.b = b;
}
}
-----------------
//B类中引用A类实例
class B {
A a;
public B(A a) {
this.a = a;
}
}
像上面这种构造器循环引用是无解的,即使强如Spring这样的框架。
- setter方法循环依赖(单实例)
//A类中引用B类实例
class A {
B b;
public void setB(B b) {
this.b = b;
}
}
-----------------
//B类中引用A类实例
class B {
A a;
public void setA(A a) {
this.a = a;
}
}
Spring框架中默认是支持这种setter方法的循环依赖的,因为setter方法的循环依赖是允许创建一个“半成品”(依赖的属性值为null)的bean。
Spring解决循环依赖原理
Spring在解决循环依赖的时候使用到了三级缓存,所谓的三级缓存也就是DefaultSingletonBeanRegistry
类中的三个map:
//一级缓存
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//二级缓存
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
我们以UserService
和IndexService
两个类为例,说明整个循环引用的解决流程
//IndexService中引用UserService对象
public class IndexService {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public IndexService() {
System.out.println("IndexService初始化");
}
}
----------------------------------------------------------------------------
//UserService中引用IndexService对象
public class UserService {
private IndexService indexService;
public IndexService getIndexService() {
return indexService;
}
public void setIndexService(IndexService indexService) {
this.indexService = indexService;
}
public UserService() {
System.out.println("userService 初始化");
}
}
在xml中配置两个bean
<!--application.xml-->
<bean id="indexService" class="top.cocoawork.service.IndexService">
<property name="userService" ref="userService"></property>
</bean>
<bean id="userService" class="top.cocoawork.service.UserService">
<property name="indexService" ref="indexService"></property>
</bean>
使用当前配置文件创建IoC容器
public class SpringApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
IndexService indexService = applicationContext.getBean(IndexService.class);
System.out.println(indexService);
}
}
开启调试,让我们直奔目的,顺着调用链,找到IoC容器创建bean的地方:
1.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(String configLocation) ;
2.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent);
3.AbstractApplicationContext#refresh();
4.AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory);
5.DefaultListableBeanFactory#preInstantiateSingletons();
6.AbstractBeanFactory#getBean(String name);
7.AbstractBeanFactory#doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly);
8.DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference);
上面第8步其实已经开始创建bean了,从这里开始慢慢调试吧💣💣,我总结下其流程:
IndexService
、UserService
创建流程:
- 尝试从一级缓存中获取
IndexService
实例,获取失败 - 将
IndexService
标记为创建状态 - 实例化
IndexService
,并将IndexService
放入到三级缓存中 - 为已经实例化的
IndexService
填充属性UserService userService
- 从一级缓存中获取
UserService
实例,显然不能获取到
- 标记
UserService
为创建状态 - 再次从一级缓存中获取
UserService
实例,再次获取失败 - 创建
UserService
实例,然后放到三级缓存中 - 为
UserService
实例填充属性IndexService indexService
- 从一级缓存中获取
IndexService
实例,获取失败 - 从二级缓存中获取
IndexService
实例,获取失败 - 从三级缓存中获取
IndexService
实例(三级缓存存储的为对象工厂,此时调用对象工厂增强) - 将获取到的
IndexService
实例放到二级缓存中 - 从三级缓存中删除
IndexService
实例 - 将
IndexService
实例填充到UserService
实例中 - 将
UserService
实例添加到一级缓存 - 从三级缓存中将
UserService
实例移除
- 此时已经得到了完整生命周期的
UserService
对象了,将其填充到IndexService
实例中 - 将
IndexService
实例添加到一级缓存 - 将
IndexService
实例从二级缓存中移除
到此,已经完成IndexService
、UserService
的创建。
在以上过程中,三级缓存的三个map中的对象变化如下:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
一级缓存:
添加 userService: UserService实例(5-11步骤)
添加 indexService: IndexService实例 (7步骤)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存:
添加 indexService: ObjectFactory(3步骤)
添加 userService: ObjectFactory(5-3步骤)
移除 indexService: ObjectFactory(5-9步骤)
移除 userService: ObjectFactory(5-12步骤)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
二级缓存:
添加 indexService: IndexService实例(5-8步骤)
移除 indexService: IndexService实例 (8步骤)
以上就是Spring中解决循环依赖的整个对象创建流程,这是耗费了一上午调试得出的结论。
其最最核心的原理就是,将实例化为初始化的半成品bean缓存起来,让需要引用它的对象先得到它,待引用它的对象完成初始化后,再用已经完成初始化的对象来填充这个半成品bean,从而都完成初始化完成bean创建。不得不说,Spring牛逼!!!