关于spring:Spring为什么要使用三级缓存解决循环依赖问题

69次阅读

共计 2907 个字符,预计需要花费 8 分钟才能阅读完成。

咱们都晓得 Spring 为了解决循环依赖应用了三级缓存

Spring 三级缓存

一级缓存 singletonObjects

用于保留 BeanName 和创立 bean 实例之间的关系,beanName -> bean instance

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

二级缓存 earlySingletonObjects

保留提前曝光的单例 bean 对象

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存 singletonFactories

保留 beanName 和创立 bean 实例之间的关系, 与 singletonObjects 不同的中央在于,当一个单例 bean 被放到这外面后,bean 在创立过程中,能够通过 getBean 办法获取到, 目标是用来检测循环援用

private final Map<String, Object> singletonFactories = new HashMap(16);

在创立 bean 的时候,首先从缓存中获取单例的 bean,这个缓存就是 singletonObjects,如果获取不到且 bean 正在创立中,就再从earlySingletonObjects 中获取,如果还是获取不到且容许从 singletonFactories 中通过 getObject 拿到对象,就从 singletonFactories 中获取,如果获取到了就存入 earlySingletonObjects 并从 singletonFactories 中移除。

为什么要应用三级缓存?

一级缓存

首先咱们看看一级缓存行不行,如果只留第一级缓存,那么单例的 Bean 都存在 singletonObjects 中,Spring 循环依赖次要基于 Java 援用传递,当获取到对象时,对象的 field 或者属性能够延后设置,实践上能够,然而如果延后设置出了问题,就会导致残缺的 Bean 和不残缺的 Bean 都在一级缓存中,这个援用时就有空指针的可能,所以一级缓存不行,至多要有 singletonObjects 和 earlySingletonObjects 两级。

两级缓存

那么咱们再看看两级缓存行不行

当初有 A 的 field 或者 setter 依赖 B 的实例对象,同时 B 的 field 或者 setter 依赖了 A 的实例,A 首先开始创立,并将本人裸露到 earlySingletonObjects 中,开始填充属性,此时发现自己依赖 B 的属性,尝试去 get(B),发现 B 还没有被创立,所以开始创立 B,在进行属性填充时初始化 A,就从 earlySingletonObjects 中获取到了实例化但没有任何属性的 A,B 拿到 A 后实现了初始化阶段,将本人放到 singletonObjects 中, 此时返回 A,A 拿到 B 的对象持续实现初始化,实现后将本人放到 singletonObjects 中,由 A 与 B 中所示意的 A 的属性地址是一样的,所以 A 的属性填充完后,B 也获取了 A 的属性,这样就解决了循环的问题。

仿佛完满解决,如果就这么应用的话也没什么问题,然而再加上 AOP 状况就不同了,被 AOP 加强的 Bean 会在初始化后代理成为一个新的对象,也就是说:

如果有 AOP,A 依赖于 B,B 依赖于 A,A 实例化实现裸露进来,开始注入属性,发现援用 B,B 开始实例化,应用 A 裸露的对象,初始化实现后封装成代理对象,A 再将代理后的 B 注入,再做代理,那么代理 A 中的 B 就是代理后的 B,然而代理后的 B 中的 A 是没用代理的 A。

显然这是不对的,所以在 Spring 中存在第三级缓存,在创建对象时判断是否是单例,容许循环依赖,正在创立中,就将其从 earlySingletonObjects 中移除掉,并在 singletonFactories 放入新的对象,这样后续再查问 beanName 时会走到 singletonFactory.getObject(),其中就会去调用各个 beanPostProcessor 的 getEarlyBeanReference 办法,返回的对象就是代理后的对象。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {    
    
        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        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));
        }

     /**
     * Add the given singleton factory for building the specified singleton
     * if necessary.
     * <p>To be called for eager registration of singletons, e.g. to be able to
     * resolve circular references.
     * @param beanName the name of the bean
     * @param singletonFactory the factory for the singleton object
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

正文完
 0