一、第一步从缓存中获取对象
在AbstractBeanFactory类中的doGetBean方法中第一步先尝试去一二三级缓存中获取我们的bean
//尝试去缓存中获取对象
Object sharedInstance = getSingleton(beanName);
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
/**
* 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
* 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态)
* 就是早期对象
*/
singletonObject = this.earlySingletonObjects.get(beanName);
/**
* 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
*/
if (singletonObject == null && allowEarlyReference) {
/**
* 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
* 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
* 暴露到三级缓存中
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//从三级缓存中获取到对象不为空
if (singletonFactory != null) {
/**
* 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
* 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
*/
singletonObject = singletonFactory.getObject();
//把早期对象放置在二级缓存,
this.earlySingletonObjects.put(beanName, singletonObject);
//ObjectFactory 包装对象从三级缓存中删除掉
this.singletonFactories.remove(beanName);
}
}
}
}
从一二三级缓存中获取,第一次进来当然都没有所以不会获取到,然后会进入创建Bean的流程.
二、创建Bean(第一次调用doGetBean)
在这个方法中开始创建我们真的bean对象
//把beanName 和一个singletonFactory 并且传入一个回调对象用于回调
sharedInstance = getSingleton(beanName, () -> {
try {
//进入创建bean的逻辑
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
在这个方法把我们创建的bean放入三级缓存
//把我们的早期对象包装成一个singletonFactory对象 该对象提供了一个getObject方法,该方法内部调用getEarlyBeanReference方法
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
在这个方法会给bean属性赋值,因为有循环依赖所以会调用创建bean的方法创建依赖的bean
//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
populateBean(beanName, mbd, instanceWrapper);
有引用对像会在这个方法调用getBean()方法去创建我们的引用对象,也就是循环到上边第一个方法去
//循环我们当前bean的属性
for (String propertyName : propertyNames) {
//判断该属性名称是不是引用对象
if (containsBean(propertyName)) {
//显示的调用getBean所有属性的名称bean显示调用BeanFactory
Object bean = getBean(propertyName);
//把我们依赖的属性添加到pvs中
pvs.add(propertyName, bean);
//注册当前bean和属性依赖bean的依赖关系
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
2.引用对象创建(第二次调用doGetBean)
引用对象创建如上一样,最终会将引用对象缓存到三级缓存,这时我们的的三级缓存中就有我们本生的对象和引用对象,最后也会走到属性赋值然后再次调用doGetBean
3.解决循环依赖(第三次调用doGetBean)
因为此时三级缓存中能得到我们本来Bean的缓存对象所以不会再次去创建,所以此时就跳出循环,并且将三级缓存存到二级缓存。
//尝试去缓存中获取对象
Object sharedInstance = getSingleton(beanName);
if (singletonFactory != null) {
/**
* 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
* 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
*/
singletonObject = singletonFactory.getObject();
//把早期对象放置在二级缓存,
this.earlySingletonObjects.put(beanName, singletonObject);
//ObjectFactory 包装对象从三级缓存中删除掉
this.singletonFactories.remove(beanName);
}
最后会通过此方法getSingleton,的钩子函数完成bean创建然后 ,将二级缓存的Bean存入一级缓存
sharedInstance = getSingleton(beanName, () -> {
try {
//进入创建bean的逻辑
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
destroySingleton(beanName);
throw ex;
}
总结
解决循环依赖的思路是 A 依赖B 然后B 依赖A然后先后创建了A 和B并缓存到了三级缓存,此时再次调用doGetBean 获取A时三级缓存有数据就跳出循环,等所有bean创建好就会放入一级缓存。