spring循环依赖一定要三级缓存吗?就算用来解决AOP,也需要三级缓存吗?

参考文章:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存

Spring是如何利用"三级缓存"巧妙解决Bean的循环依赖问题

个人理解:
1、其实把getEarlyBeanReference生成的对象直接保存到二级缓存,无需三级缓存用ObjectFacotry封装原始bean也可以解决循环依赖。三级缓存感觉纯粹是为了延迟调用aop逻辑而已。

2、其实把getEarlyBeanReference生成的对象直接暴露到一级缓存也是可以的。只要引用的地址不变,谁要用就提前给谁。初始化动作可以后面慢慢做。只要引用不变,它初始化完成后,所有引用它的bean都自然而然的能得到完成的该bean。可能spring担心一级缓存既用来存放单例bean,又用来存放提前暴露的bean,会引起混乱。所以,上面徐庶老师说的,只要胆子大,一级缓存够用。解决循环依赖的核心,不在乎几级缓存,而在于提前暴露引用地址即可。

一、先交代下什么是循环依赖,什么是三级缓存

循环依赖:A依赖B,B依赖A
三级缓存:

//一级缓存,用来存放初始化完成的单例bean
Map singletonObjects;
//二级缓存,用来存放提前暴露的原始bean
Map earlySingletonObjects;
//三级缓存,用来存放 “包装提前暴露的原始bean”的ObjectFactory对象
Map> singletonFactories;

有人可能会问,提前暴露的对象已经存放在二级缓存了,为啥还要在三级缓存中存放呢?下文会详细解释。

二、循环依赖的解决

    protected  T doGetBean(
            final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
        .....//省略

getBean的时候,一上来就先去拿一下提前暴露的bean对象。

getSingleton方法如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //先去一级缓存拿。新创建的bean,这里一定拿不到
        Object singletonObject = this.singletonObjects.get(beanName);
        //拿不到初始化完成的bean,且该bean正在被创建中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
        //优先去二级缓存拿,如果没有再去三级缓存拿。有了,就直接返回。
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
            //最后一步,去三级缓存拿
                    ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
        //调用三级缓存ObjectFactory的getObject得到提前暴露的对象。
                        singletonObject = singletonFactory.getObject();
        //放到二级缓存中,然后删除三级缓存。可见:同一个提前暴露的bean,只能要么在三级缓存,要么在二级缓存。
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

我们肯定会有疑问,不就去拿一个尚未初始化完成的bean对象而已嘛?有一个地方存一下,这里取出来,不就行了嘛。为啥非要在搞一个三级缓存呢?
想知道三级缓存做了啥,就要看下三级缓存的ObjectFactory.getObject到底做了啥?

三、三级缓存的ObjectFactory.getObject到底做了啥?

提前暴露对象的代码在doCreateBean里面。如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //实例化bean,尚未初始化
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
        mbd.resolvedTargetType = beanType;

        ..... //省略

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
      //判断是否可以提前暴露。判断条件 = 是否单例 && 是否允许(默认true) && 是否创建过程中
        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");
            }
              //这里把尚未初始化的bean,包装成ObjectFactory对象传递给addSingletonFactory方法
            addSingletonFactory(beanName, new ObjectFactory() {
                @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);
            }
        }
        ...//省略
 
 

我们看下解决循环依赖的核心方法addSingletonFactory,如下:

protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
        //bean已经被其他线程初始化完成放到一级缓存了,这里也没必要放到三级缓存
            if (!this.singletonObjects.containsKey(beanName)) {
                //放到三级缓存,然后删除二级缓存(以防有值)
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

可以看到,这里仅仅是把提前暴露的bean封装成的ObjectFactory,放到三级缓存中。

是不是还是不明白为啥需要三级缓存?我们看下上面添加的匿名内部类ObjectFactory的实现。

  new ObjectFactory() {
        @Override
        public Object getObject() throws BeansException {
                return getEarlyBeanReference(beanName, mbd, bean);
           }
     }
 
 

答案就在getEarlyBeanReference方法里面,如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    //对于有SmartInstantiationAwareBeanPostProcessor,特殊处理
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    if (exposedObject == null) {
                        return null;
                    }
                }
            }
        }
        return exposedObject;
    }

这个地方专门用来特殊处理SmartInstantiationAwareBeanPostProcessor接口,说明getEarlyBeanReference也是一个拓展点,作用在这里的生命周期。getEarlyBeanReference的实现到底是啥呢?我们找个最常见的实现类,AbstractAutoProxyCreator。看下它的方法:

@Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            this.earlyProxyReferences.add(cacheKey);
        }
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

很神奇的地方,我们在看下AbstractAutoProxyCreator.postProcessAfterInitialization方法:

@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

getEarlyBeanReference和postProcessAfterInitialization,何其相似啊!!!
postProcessAfterInitialization我们知道,它只会在bean的所有初始化方法完成后,调用aop生成代理类。但是getEarlyBeanReference竟然对尚未初始化完成的bean,提前进行了aop代理。Why?不用初始化完成,就能代理吗?

一直想不通,最后终于想通了。既然尚未初始化完成的bean都可以提前注入到其他bean里面,为啥就不能提前AOP呢?我们用的是bean的引用,只要这个引用不变,至于引用所指向的对象啥时候初始化完,其实无所谓。其他bean也只是持有的是这个bean的引用,同理AOP代理也是仅仅持有target bean的引用。所以,所有使用到bean的地方,只要实例化完成生成了引用地址,只要这个地址不变,就可以把这个bean当做成熟的bean使用。等整个容器启动完成,这些bean自然而然的就初始化好了,所有引用这个bean的Bean也自然而然的就可以使用了。

image.png

到这里大家应该清楚了,为啥需要三级缓存了吧。如果你依赖的对象是AOP代理,那么就需要用到第三级缓存暂存ObjectFactory。

你可能又会问,为啥这里不直接把getEarlyBeanReference生成的对象,放到二级缓存里面呢?这样不也节省了三级缓存嘛?为啥非要在getBean.getSingleton里面去调用getObject呢?

这个问题问得好,其实我也觉得可以。我目前也没答案,可能spring基于效率的考虑吧。

个人理解:
1、其实把getEarlyBeanReference生成的对象直接保存到二级缓存,无需三级缓存用ObjectFacotry封装原始bean也可以解决循环依赖。三级缓存感觉纯粹是为了延迟调用aop逻辑而已。
2、其实把getEarlyBeanReference生成的对象直接暴露到一级缓存也是可以的。只要引用的地址不变,谁要用就提前给谁。初始化动作可以后面慢慢做。只要引用不变,它初始化完成后,所有引用它的bean都自然而然的能得到完成的该bean。可能spring担心一级缓存既用来存放单例bean,又用来存放提前暴露的bean,会引起混乱。所以,上面徐庶老师说的,只要胆子大,一级缓存够用。解决循环依赖的核心,不在乎几级缓存,而在于提前暴露引用地址即可。

你可能感兴趣的:(spring循环依赖一定要三级缓存吗?就算用来解决AOP,也需要三级缓存吗?)