上一篇文章,整体介绍了spring ioc容器初始化过程(starter->refresh->registerBeanDefinition->doCreateBean->populateBean)
但仅限于常规流程。一些特殊情况,比如“对象循环应用”spring会如何处理——这是本篇要解答的问题。
1.spring如何解决对象的循环依赖问题?
什么是循环依赖?
@Component
class A{
@Autowire
B b;
}
@Component
class B{
@Autowire
A a;
}
上面的代码就是比较简单的循环依赖:A引用B的同时,B也引用了A
这种情况初始化时会遇到什么问题?
创建A的时候,发现引用了B,转而去创建B;创建B的时候,又发现引用了A,转而去创建A产生了死循环……
怎么解决?
解决方式也简单,只要声明一个集合(如:creatingSet),需要创建对象时先去检查集合中有没有,没有再创建,并把对象放入集合中。
这样,A、B对象的初始化过程就变成了:
- 检查creatingSet中有没有对象A,没有则创建A,并记录下来creatingSet.set(A)
- 发现A引用了B转而创建B,creatingSet中没有对象B,创建并记录creatingSet.set(B)
- 发现B引用A,此时creatingSet中存在对象A,不必再创建直接赋值
spring也是这个思路,它是通过singletonsCurrentlyInCreation
集合记录正在被创建的对象名,而用三级缓存存放对象。
spring怎么解决?
用一种更简单的循环依赖情况举例:
@Component
class A{
@Autowire
A a1;
}
我们来看看A对象的初始化流程
getBean(beanName){
// -- 1.增加标记【singletonsCurrentlyInCreation.set(beanName)】
beforeSingletonCreation(beanName);
// -- 2.构造函数创建对象【A对象创建了,它的属性a1为空】
instanceWrapper = createBeanInstance(beanName, mbd, args);
// -- 3.放入三级缓存,移除二级【三级缓存中存在a1的工厂】
// ## 只有循环依赖才会执行到此,代理动作提前了
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// -- 4.属性初始化,递归调用getSingleton(String,boolean)
// 【初始化属性a1 开始】
populateBean();
⬇⬇⬇⬇⬇
Object getSingleton(String beanName, boolean allowEarlyReference)
Object getSingleton(String beanName, boolean allowEarlyReference) {
// == a.尝试从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
// == b.一级缓存没有 && 但正在创建(递归时会进入if)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// ** 通过三级缓存获取到的工厂创建
singletonObject = singletonFactory.getObject();
// 加入二级,移除三级
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
// == c.递归时,执行完`代码2`的if,返回二级缓存对象
return singletonObject;
}
⬆⬆⬆⬆⬆
// 【初始化属性a1 结束:二级缓存中存在a1对象,三级缓存的a1工厂移除】
populateBean();
// ## 4.5 调用对象后置处理器【代码3已经提前做了代理,此处不执行】
// ## initializeBean(beanName, exposedObject, mbd)
// -- 5.对属性进行注入【A对象的a1属性有值】
applyPropertyValues(beanName, mbd, bw, pvs);
// -- 6.移除标记【singletonsCurrentlyInCreation.remove(beanName)】
afterSingletonCreation(beanName);
// -- 7.移除三级、移除二级,对象存入一级缓存【完结撒花,A对象初始化完成】
addSingleton(beanName, singletonObject);
代码3
、代码4
、代码4.5
三处发生了变化。
以上就是spring解决对象循环应用问题的方式。
2.spring为什么设计了二级、三级这两级缓存解决循环依赖?
这个问题其实又可以分为两个问题。
A.只用二级缓存:代码3
位置直接创建代理对象是否可行?
答:可以,但不符合spring理念。
spring的理念是:Bean在生命周期的最后一步完成代理,而不是实例化之后立马完成代理。
总的来说,解决循环依赖有两种思路:
- 不管有没有循环依赖,提前创建好代理对象,将代理对象放入缓存;等出现循环依赖时,直接从缓存获取。
本节探讨的问题——只用二级缓存:代码3
位置直接创建代理对象——就是这种方式。
甚至再糙一点,二级缓存也省了,都放入一级缓存同样能解决。(你想,反正最终这些对象都会加载完)
- 不提前创建代理对象,在出现循环依赖时才生成代理对象。
显然,spring选择了这种方式。
B.只使用三级缓存,每次通过工厂创建是否可行?
答:不行。
这个比较容易理解,每次通过工厂创建对象,会破坏单例。
3.什么是FactoryBean?
除了普通对象之外,spring还为使用者提供了一个FactoryBean接口,用来创建复杂对象。(算是一个扩展点)
扩展点
如果一个FactoryBean对象注入到spring中,从SpringContext获取时得到的是一个调用FactoryBean.getObject()创建的普通对象。
有点绕嘴,举例说明一下:
// 声明一个Test对象
class Test{}
// 声明一个TestFactoryBean对象,实现FactoryBean接口
class TestFactoryBean implements FactoryBean<Test> {
@Override
public CostForm getObject() throws Exception {
Test test = new Test();
return test;
}
@Override
public Class<?> getObjectType() {
return Test.class;
}
}
上面的例子中,通过SpringContextUtil.getBean("testFactoryBean")
获取的对象不是TestFactoryBean
,而是Test
。
那么如何获取到TestFactoryBean呢,name增加“&”前缀
即可。
TestFactoryBean factoryBean = SpringContextUtil.getBean("&testFactoryBean")
Test test = SpringContextUtil.getBean("testFactoryBean");
源码实现
为什么会如此?通过源码来寻找答案。
- TestFactoryBean对象的注入
// 对象初始化过程有这么一段逻辑
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
preInstantiateSingletons(){
// 如果是FactoryBean对象,会增加“&”前缀注入
if (isFactoryBean(beanName)) {
Object bean = getBean("&" + beanName);
}
}
- Test对象的注入
Spring对FactoryBean对象处理的执行入口在这里:
// 前面分析过的对象初始化逻辑
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
// ## 对象创建后,做特殊处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
观察具体做了什么处理:
Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// -- 非FacotryBean对象,直接返回
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
// -- 通过FactoryBean获取对象
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
⬇⬇⬇⬇⬇⬇
// ## 最终会调用这里,执行FatoryBean的getObject方法
object = factory.getObject();
⬆⬆⬆⬆⬆⬆
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}