共计 4148 个字符,预计需要花费 11 分钟才能阅读完成。
上一篇文章,整体介绍了 spring ioc 容器初始化过程(starter->refresh->registerBeanDefinition->doCreateBean->populateBean)
但仅限于惯例流程。一些非凡状况,比方“对象循环利用”spring 会如何解决——这是本篇要解答的问题。
1.spring 如何解决对象的循环依赖问题?
留神:
构造函数型循环依赖,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);
}
附录
P6-P7 常识合辑