spring -- 循环依赖之SingletonObjects ,earlySingletonObjects和singletonfactories一级,二级和三级缓存 和 singletonsCurrentlyInCreation

前提:允许bean提前暴露(属性还没有赋值,有空的类对象) ,允许循环依赖 (循环依赖才往三级缓存中添加数据)

 

循环依赖的情况 一 (属性中循环依赖)

例子:

循环依赖

@Component
public class CircleRefA {

    @Autowired
    private CircleRefB circleRefB;
}
@Component
public class CircleRefB {
    @Autowired
    private CircleRefA circleRefA;

    @Autowired
    private CircleRefC circleRefC;
}
@Component
public class CircleRefC {
    @Autowired
    private CircleRefA circleRefA;
}

假设spring中处理顺序为 CircleRefA (beanName为circleRefA ) -》  CircleRefB (beanName为circleRefB )   -》  CircleRefC (beanName为circleRefC) 

先处理CircleRefA

先 getbean(circleRefA),这时一级缓存singletonObjects中没有对应的类,且 singletonCurrentInCreation 中没有 circleRefA , getbean(beanName) 返回 null ,调用getBean(circleRefA , ObjectFactory)  。此时先往 singletonCureentInCreation 中加入 circleRefA(表示正在创建 circleRefA 对应的实例) 。先创建一个属性值为空的A ,称为 早期的bean实例,调用addSingletonFactory ,往在三级缓存中添加circleRefA 对应的 ObjectFactory(此对象,就理解为时 A实例的包装)再进行populateBean进行IOC填充circleRefA 。发现A中有CircleRefB的引用,进行CircleRefB实例化,进行 先 getbean(circleRefB)

 

在进行getbean(circleRefB),前面的操作一样,后面再进行opulateBean进行IOC填充circleRefB 时,发现B中有CircleRefA的引用,进行getBean(circleRefA) (需要说明的时,属性的编写是认为的,此处可能先进行getBean(circleRefC),分析一样)进行getBean(circleRefA)时,一级缓存singletonObjects中没有对应的类,singetonCureentInCreation中有 circleRefA ;则查找二级缓存earlySingletonObjects ,没有数据;再查找三级缓存,有数据 ,将数据填充到二级缓存earlyObjects , 删除三级缓存,返回给B实例进行填充。发现有C类属性,进行 getbean(circleRefC)

 

在进行getbean(circleRefC),前面操作同getBean(circleRefA)。后面再进行opulateBean进行IOC填充circleRefC 时,发现C中有CircleRefA的引用,进行getBean(circleRefA)  。此时getBean(circleRefA) ,是从二级缓存中拿到的数据 ,返回A对应的类。

 

完成 C 的实例化,一级缓存singletonObjects中添加C的实例,删除 singletosCurrentlyInCreation中的circleRefC ,删除三级级缓存中 circleRefC,删除二级缓存 circleRefC。再递归完成 B , A的实例化(同C的实例化)。

 

 

说明:

singletonCureentInCreation 中存bean的名称,表示实例正在创建

这里为什么有一个三级缓存singletonfactories,就是放置早期的bean实例

为什么有一个二级缓存earlySingletonObjects,其实也是放置早期的bean实例。多次拿  早期的bean实例 这一直接冲这里面拿。

直接放在三级缓存singletonfactories 中 ,不放在earlySingletonObjects中,功能上不是也可以吗?是的

 

但是,其实获取三级缓存的方式是

源码 :  

//添加 三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 如果 一级缓存 中不存在
            if (!this.singletonObjects.containsKey(beanName)) {
                // 设置 三级缓存 ,缓存 的 是  singletonFactory 对象
                this.singletonFactories.put(beanName, singletonFactory);
                // 删除 二级缓存
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

获取三级缓存

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
singletonObject = singletonFactory.getObject();
其中
@FunctionalInterface
public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return the resulting instance
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

 

三级缓存是一个包装bean的实例的对象,singletonFactory.getObject() 是 ,其实是调用的  getEarlyBeanReference(beanName, mbd, bean) ,

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

这个地方就是预留了一个点位,进行操作 早期的类对象 ,将经过处理(有处理过程,可以放回原始的bean早期类对象,也可能不是,实现BeanPostProcessor接口,重写getEarlyBeanReference就行,完全可以自定义修改) 。

 

就是我们说的三级缓存中,其实可以对 早期的类对象 进行自定已处理 。将处理完的对象 ,放在二级缓存中,若还有循环依赖的 处理 ,拿的是 经过处理的   早期的类对象 

 

循环依赖的情况 二 (多例循环依赖scope 为 prototype )

往三级缓存中添加数据源码

        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

都不是单例,不会往三级缓存singletonfactories中添加数据。每次创建的都是新对象,没必要缓存

此时根据 prototypesCurrentlyInCreation 判断是不是再创建的 ,循环依赖的二次报错。

创建是,会现在查

            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
再防止 this.prototypesCurrentlyInCreation.set(beanName);

 

循环依赖的情况 三 (构造函数的循环依赖)

@Component
public class CircleRefAA {

    @Autowired
    public CircleRefAA(CircleRefBB circleRefBB){
        System.out.println("---------CircleRefAA----------");
    }
}
@Component
public class CircleRefBB {

    @Lazy
    @Autowired
    public CircleRefBB(CircleRefAA circleRefAA){
        System.out.println("---------CircleRefBB----------");
    }
}

这个时候正在创建 早期的bean实例,三级缓存中没有存数据 ,重复向 singletonsCurrentlyInCreation 中加数据,报错。

        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

 

循环依赖的情况 四 (构造函数的循环依赖加@Lazy)

网上说构造函数的循环依赖加@Lazy可以解决

--未完待续

 

说的比较大白话,中间的一些话语 也不是专业术语,能力有限,单大体上就是 




posted on 2021-02-28 21:24  xingshouzhan  阅读(4434)  评论(0编辑  收藏  举报

导航