title: Spring 框架源码解读7
date: 2020/04/17 16:09
本节内容 & 思考题
我们之前的依赖注入的版本是不支持循环依赖的,我们本节就将其改造成支持的。
Spring 中是否支持循环依赖?
支持
为什么 2个对象都是原型的就不支持了呢?
因为 A 中引用 B 就需要创建 B,B 引用了 A 就要创建 A1 ...
怎样关闭 Spring 的循环依赖?
打破下面三个条件的随便哪个
// 满足单例 + 开启了循环依赖(默认为true) + bean在singletonsCurrentlyInCreation集合中时
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
谈谈 Spring 的循环依赖具体实现?
三级缓存
修改 AbstractBeanFactory#getBean()
public abstract class AbstractBeanFactory implements BeanFactory {
...
// 二级缓存:维护早期暴露的Bean(只进行了实例化,并未进行属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
/**
* 根据名称获取对应的 bean (工厂方法模式)
* <p>
* 注:没有对原型做处理,如果2个对象都是原型,则会进入死循环从而堆栈溢出
*/
@Override
public Object getBean(String name) {
// 先从一级缓存中获取
if (sharedInstanceCache.containsKey(name)) {
return sharedInstanceCache.get(name);
}
// 如果一级缓存中没有,则从二级缓存中获取(防止循环引用)
if (earlySingletonObjects.containsKey(name)) {
return earlySingletonObjects.get(name);
}
// 如果二级缓存中没有,则获取 bd 创建
BeanDefinition bd = this.getBeanDefinition(name);
if (ObjectUtil.isNotNull(bd)) {
Object bean = this.createBean(bd);
// 判断是否是单例,如果是则加入一级缓存中
if (bd.getScope() == BeanDefinition.ScopeEnum.SINGLETON) {
sharedInstanceCache.put(bd.getName(), bean);
}
return bean;
}
// 如果 bd 为空,而且没有父 bf,则抛出异常
if (ObjectUtil.isNull(parentBeanFactory)) {
throw new RuntimeException("获取的bean不存在!");
}
// 从父 bf 中获取
return parentBeanFactory.getBean(name);
}
/**
* 根据 bd 创建对象
* <p>
* 1)创建bean 日后需要对有参构造进行扩展
* 2)注入属性(Spring 源码中 2 是在 3456 的后面)
* 3)调用部分 Aware 的方法
* 4)后置处理器的前置方法
* 5)执行初始化操作
* 6)后置处理器的后置方法
* 7)注册销毁的处理
*/
private Object createBean(BeanDefinition beanDefinition) {
// 1、创建 bean
BeanWrapper beanWrapper = this.createBeanInstance(beanDefinition);
// 1.1 将还没有注入属性的 bean 放入二级缓存中
if (beanDefinition.getScope() == BeanDefinition.ScopeEnum.SINGLETON) {
earlySingletonObjects.put(beanDefinition.getName(), beanWrapper.getWrappedInstance());
}
...
Spring 0.9
Spring 0.9 中和我们实现的方式基本相同
Spring 5.0 中通过三级缓存实现
本部分参考:https://juejin.im/post/5dbb9fdef265da4d4c202483
当获取 bean 的时候,Spring 会先调用 getSingleton() 方法
// 它在Bean开始创建时放入 beanName,创建完成时会将其移出
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Cache of singleton objects: bean name --> bean instance */
一级缓存:维护着所有创建完成的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
二级缓存:维护早期暴露的Bean(只进行了实例化,并未进行属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
三级缓存:维护创建中Bean的ObjectFactory(解决循环依赖的关键)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
分析: Spring首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则通过三级缓存获取,即通过singletonFactory.getObject()。如果获取到了,将其存入二级缓存,并清除三级缓存。
如果缓存中没有bean对象,那么Spring会创建Bean对象,将实例化的bean提前曝光,并且加入缓存中。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
......
if (instanceWrapper == null) {
//这个是实例化Bean的方法,会调用构造方法,生成一个原始类型的Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 提前曝光这个实例化的Bean,方便其他Bean使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 满足单例 + 开启了循环依赖(默认为true) + bean在singletonsCurrentlyInCreation集合中时
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将bean加入三级缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性注入,这里可能发生循环依赖
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
// 由于AService提前暴露,会走这段代码
if (earlySingletonExposure) {
// 从二级缓存中拿出AService(这个对象其实ObjectFactory.getObject()得来的,可能是个包装类,而exposedObject可能依然是实例化的那个bean,这时为保证最终BService中的AService属性与AService本身持有的引用一直,故再次进行exposedObject的赋值操作,保证beanName对应实例唯一性。)
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
// ...........
return exposedObject;
}
分析:当通过无参构造,获得一个实例化bean时,Spring会将其提前曝光,即在实例化后注入属性前将其加入三级缓存中。下面以AService和BService相互依赖为例,说明依赖排除过程。
- AService实例化后,在注入属性前提前曝光,将其加入三级缓存singletonFactories中,供其他bean使用;
- AService通过populateBean注入BService,从缓存中获取BService,发现缓存中没有,开始创建BService实例;
- BService实例也会在属性注入前提前曝光,加入三级缓存中,此时三级缓存中有AService和BService;
- BService在进行属性注入时,发现有AService引用,此时,创建AService时,会先从缓存中获取AService(先从一级缓存中取,没有取到后,从二级缓存中取,也没有取到,这时,从三级缓存中取出),这时会清除三级缓存中的AService,将其将其加入二级缓存earlySingletonObjects中,并返回给BService供其使用;
- BService在完成属性注入,进行初始化,这时会加入一级缓存,这时会清除三级缓存中的BService,此时,三级缓存为空,二级缓存中有AService,一级缓存中有BService;
- BService初始化后注入AService中,AService进行初始化,然后通过getSingleton方法获取二级缓存,赋值给exposedObject,最后将其加入一级缓存,清除二级缓存AService;
从上述分析可知,singletonFactories即三级缓存才是解决循环依赖的关键,它是一个桥梁。当AService初始化后,会从二级缓存中获取提前暴露的对象,并且赋值给exposedObject。这主要是二级缓存的对象earlySingletonReference可能是包装类,BService持有的引用就是这个earlySingletonReference,赋值后保证beanName对应实例唯一性,这点回味无穷。
你是不是和我一样有疑问,为啥要有第 3 级缓存,不是已经有了 bean 对象了吗,直接放进二级缓存里不行吗?
原因在 getEarlyBeanReference(beanName, mbd, bean) 方法中:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
实际上,它是对 bean 做了一步操作,如果没有 SmartInstantiationAwareBeanPostProcessor (默认好像就没有),就相当于直接返回了 bean。
那么 SmartInstantiationAwareBeanPostProcessor 是啥,他要干啥呢:
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
// 预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null
Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;
// 选择合适的构造器,比如目标对象有多个构造器,在这里可以进行一些定制化,选择合适的构造器
// beanClass参数表示目标实例的类型,beanName是目标实例在Spring容器中的name
// 返回值是个构造器数组,如果返回null,会执行下一个PostProcessor的determineCandidateConstructors方法;否则选取该PostProcessor选择的构造器
Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;
// 获得提前暴露的bean引用。主要用于**解决循环引用的问题**
// 只有单例对象才会调用此方法
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
}