21.循环依赖问题答疑

126 阅读6分钟

每级缓存放的都是什么东西?

1级缓存放的是完整的Bean

2级缓存方法的是实例化好的代理Bean

但是没有经历过初始化即

1.invokeAwareMethods,设置Spring的上下文组件

2.BeanPostProcessor 的 before 方法

3.invokeInitMethods

4.BeanPostProcessor 的 after 方法

3级缓存放的是ObjectFactory

为什么需要第二级缓存?

分层思想

singletonObjects存放的是成品 代理对象且已经填充属性。

earlySingletonObjects存的是半成品 是代理对象 但是还未填充属性。

singletonFactories存的是工厂对象ObjectFactory,ObjectFactory→getEarlyBeanReference。

保证原生对象和增强对象是一个对象

如果没有AOP:直接将工厂对象的getObject方法返回的对象直接放到1级缓存。

如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题

如果有AOP,两级缓存是无法解决的,因为不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存即二级缓存来保存产生的代理对象。

假设如下场景:

如果没有二级缓存。

A依赖B B依赖A C依赖A

假设A的工厂对象已经放到了三级缓存。

然后B来实例化,获取到了A的工厂对象,并调用了工厂对象的getObject方法返回1个ProxyA1。

然后C来实例化,获取到了A的工厂对象,并调用了工厂对象的getObject方法返回1个ProxyA2。

那如何保证获取的ProxyA1和ProxyA2是1个对象呢?

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     //是否应创建早期引用 allowEarlyReference is true
    Object singletonObject = this.singletonObjects.get(beanName);
    //一级缓存中该bean为空 且 当前 bean 正在创建才能去2级缓存拿
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //2级缓存中获取
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        //3级缓存中获取 获取的是一个ObjectFactory
                        ObjectFactory<?> singletonFactory
                                            =this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            //调用ObjectFactory的getObject方法获取对象
                            singletonObject = singletonFactory.getObject();
                            //放入2级缓存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            //从3级缓存移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

扩展的思想

如果我想要在读取二级缓存:earlySingletonObjects对象时统一增加一些日志或者其他处理动作,如何解决?

在读取earlySingletonObjects对象实例后,对实例进行回调某些接口方法?

当然没问题,可以看到spring对于非单例bean也采用该方法来处理前置、后置动作的回调。

但是每次修改回调动作都要对所有读取earlySingletonObjects对象的地方进行修改,岂不是很恶心?每次增加一个读取earlySingletonObjects对象的动作都要记得加入相应的回调。是不是很容器遗漏和出错?那么如果我们缓存的是一个接口而不是一个单例对象问题是不是就解决了呢?只需要对接口增加对应的动作即可,例如:

为什么要第三级缓存?

严格来讲,第三级缓存并非缺它不可,因为可以提前创建代理对象,提前创建代理对象只是会节省那么一丢丢内存空间,

并不会带来性能上的提升,但是会破环 Spring 的设计原则。

Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成,懒加载)

所以 Spring 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的。

什么时候放入一级缓存的呢?

在第二次调用DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 如果当前对象正在被销毁
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException();
                }
                // 验证完要真正开始创建对象 
                // 先标识该bean正在被创建 因为bean创建过程复杂步骤很多需要标识
                beforeSingletonCreation(beanName);
                // 是否在singletonObjects里存在 存在就不再put
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = 
                                (this.suppressedExceptions == null);
                //
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    /***
                     * singletonFactory 即 AbstractBeanFactory
                     * singletonFactory是函数式接口
                     * 注意是在这里创建的bean
                     * 具体创建过程看 singletonFactory
                     */
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : 
                                            this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    //加入一级缓存 从二三级缓存移除
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

B依赖的是A的代理✔还是A的实例?

A依赖B,B依赖A.创建A的时候需要B.转而去创建B,创建B的时候又依赖A.

此时B拿的是A的代理还是A的实例?

依赖的是A的代理。B依赖A去自动转配A的时候会从三级缓存拿ObjectFactory

即通过AbstractAutowireCapableBeanFactory#getEarlyBeanReference

//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) {
                    //org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.getEarlyBeanReference
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

然后会调用所有的SmartInstantiationAwareBeanPostProcessor 其中有1个AnnotationAwareAspectJAutoProxyCreator继承自AbstractAutoProxyCreator 它的getEarlyBeanReference方法就是父类AbstractAutoProxyCreator的getEarlyBeanReference方法

代码如下:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    
                    //AbstractAutoProxyCreator.getEarlyBeanReference
                    SmartInstantiationAwareBeanPostProcessor ibp = 
                                (SmartInstantiationAwareBeanPostProcessor) bp;
                    
                    exposedObject = 
                            ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
@Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        //是不是很熟悉
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

截图为证,注意$proxy

image.png

循环依赖流程图

image.png

如果发生循环依赖的对象,不需要代理的话,只需要二级缓存足矣解决所有问题,但是当存在代理之后就无法解决了,必须要使用三级缓存来解决。

image.png

想解决AB循环依赖的问题,必须保证不会出现第二次创建A对象这个步骤,也就是说从容器中获取A的时候必须要能够获取到。

image.png

image.png

image.png

参考链接:blog.csdn.net/likun557/ar…

当某个 bean 进入到 2 级缓存的时候,说明这个bean的早期对象(代理对象)被其他 bean 注入了,也就是说,这个 bean 还是半成品,还未完全创建好的时候,已经被别人拿去使用了。

A 依赖 B , B 依赖 A和C , C 依赖 A*

**这种情况就直接从二级缓存里面获取,从而解决了循环依赖。(这里解释了为什么不直接在二级缓存里存放lambda表达式,因为同一个lambda表达式每执行一次,就会生成一个新的代理对象,不能保证单例)

所以必须要有 3 级缓存,2 级缓存中存放的是早期的被别人使用的对象,如果没有 2 级缓存,是无法判断这个对象在创建的过程中,是否被别人拿去使用了。

1.3级缓存放的是ObjectFactory,3级缓存存放的有刚创建的bean的实例的引用,[ 此时bean刚刚实例化 ]

2.调用ObjectFactory#getObject(),getObject()会将bean从3级缓存移除,加入2级缓存,加入2级缓存的是bean的代理对象的引用。 [bean实例被代理,代理对象存在对bean的引用]

3.接着对bean做初始化的工作。

那么只要保证放入3级缓存的bean的实例的引用和加入2级缓存的是bean的代理对象的引用是同一个引用即可。

参考:www.jianshu.com/p/fe77ec92b…