两个对象都是单实例的情况下,且通过set方式进行注入,才能成功
下面设计一个场景
public class A {
private B b;
}
public class B{
private A a;
}
我调用下面的代码
A a = (A)BeanFactory.getBean("a");
因为是第一次创建,所以 getSingleton() 方法不会获得对象,执行doCreateBean()方法创建对象,在完成实例化后,通过populateBean() 方法进行属性的填充,因为A对象中有B作为属性,B为自定义类型,最终一定会FactoryBean.getBean("b"),B完成实例化后,因为其中有A属性,要进行属性的填充,FactoryBean.getBean("a"),那么此时我们能不能获取到a?
此时调用getBean("")方法,首先会调用 getSingleton() 方法获取对象,那么此时单例池中有A这个对象吗,我们知道,只有对象在初始化方法结束之后,即执行完initializeBean之后,即执行完singletonFactory.getObject(),这个回调函数的方法之后才会执行addSingleton(beanName, singletonObject),方法,将创建完毕后的对象放入单例池中作为缓存,此时的A对象才刚刚经过实例化,不可能被放入单例池中,所以A还是会进入craeteBean方法。
进入createBean方法之后,表明我要创建一个A的新对象,但这本身就是错的,因为A是单实例对象,现在我又要创建一个新的A对象,显然这不是我们想要的结果,因此我们要尝试解决这个问题。
Spring尝试将未创建完成的对象也进行存储,但是不能存储在 DefaultListableBeanFactory 的 singletonObjects 中,因为singletonObjects是单例池,存储的是已经创建好的单例对象,所以Spring又在 DefaultListableBeanFactory 中提供了另外两个Map:earlySingletonObjects 和 singletonFactories。
显然,这个将仅完成实例化的对象进行存储的操作,一定发生在populateBean之前,createBeanInstance方法之后。我们发现是这个方法addSingletonFactory
我们进入这个方法之后,我们发现,放入singletonFactories这个Map中的对象,并不是未完成创建的对象,而是一个工厂,而这个工厂是由谁创建的。是一个lamda表达式创建的,即实质上放置了一个方法 。
那么为什么我不直接放未创建完成的对象,我要放一个方法呢,就目前的场景来看,固然是没有问题的,我可以直接放入未完成的对象进去,但是如果我的A是一个被AOP处理过的对象呢,同时B也是被处理过的对象,那么我Factory.getBean("a"),最终获取到的并不是A对象,获取到的是a的代理对象ProxyA,所以此时我若直接将未创建完成的对象直接放入Map中,让B来获取A对象,B获取到的是原始对象A,与我们的预期不符,所以要放入一个方法,这个方法的作用也呼之欲出,通过BeanPostProcess来创建代理对象,我要在这个方法里做代理,做AOP,同时我会将创建好的代理对象放入代理的缓存池中(避免后续A正常初始化的时候重新创建代理对象)。
那么此时B已经从singletonFactories这个缓存中获取到了A的代理对象,意味着我已经完成了populateBean()方法,即使A不完整,但是我也可以开始初始化了,之后正常的执行初始化方法,创建代理类,最后B对象创建完毕,给A填充属性。
A进入初始化方法,此时A不会再创建代理,直接从代理缓存池中拿即可,最终完成对象的创建。
此时,我们再审视一下getSington方法
我们已经知道,在FactoryBean.getBean("b")的过程之前,A未完成的对象已经跟随getEarlyBeanReference存入了singletonFactories这个Map中,此时b填充属性的过程中调用getBean(”a“)之后再调用getSington(a)这个方法,会执行到上方红色箭头所指向的地方,此时会执行getObject()方法,执行那个方法的回调,之后获取到的a对象(可能是A的代理,不做AOP,就是普通的a)放入earlySingletonObjects这个Map中,再将singletonFactories中的对象去掉。
最后B完成初始化,将B放入SingletonObjects中,清空其他两池子。