Spring 循环引用(二)源码分析

Spring 循环引用(二)源码分析

Spring 系列目录(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

Spring 循环引用相关文章:java

  1. 《Spring 循环引用(一)一个循环依赖引起的 BUG》:http://www.javashuo.com/article/p-bwuysaxv-w.html
  2. 《Spring 循环引用(二)源码分析》:http://www.javashuo.com/article/p-gqplicns-ce.html

1、Spring 中单例 bean 的管理

Spring 对单例 bean 的管理都是在 DefaultSingletonBeanRegistry 中完成的,这里会涉及到其内部所使用的几个内部属性:spring

// 1.1 保存最终建立成功的单例 beanName -> beanInstance
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 1.2 中间变量,beanName -> Objectfactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 1.3 中间变量,bean 还在建立的时候就能够获取,用于检测循环引用 
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

这里涉及用于存储 bean 的不一样的 Map,可能让人感到崩溃,简单解释以下:缓存

  1. singletonObjects:用于保存 beanName 和建立 bean 实例之间的关系。beanName -> beanInstance
  2. singletonFactories:用于保存 beanName 和对象工厂的引用关系,一旦最终对象被建立(经过 objectFactory.getObject()),此引用信息将删除。beanName -> Objectfactory
  3. earlySingletonObjects:用于保存 beanName 和建立的原始 bean 的引用关系,注意这里是原始 bean,即便用工厂方法或构造方法建立出来的对象,一旦对象最终建立好,此引用信息将删除。 与 singletonObjects 的不一样之处在于,此时 bean 还在建立过程当中,并且以后还能够进行加强,也就是代理后这两个 bean 就不是同一个了。能够经过 getBean 方法获取到了,其目的是用来检测循环引用。

从上面的解释,能够看出,这 singletonFactories 和 earlySingletonObjects 都是一个临时的辅助状态。在全部的对象建立完毕以后,此两个对象的 size 都为 0。那么再来看下这两个对象如何进行协做:app

(1) 方法1:建立单例对象ide

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 1. 将这个 bean 添加到 singletonsCurrentlyInCreation 集合中,这样就能够判断 bean 是否存在建立
            beforeSingletonCreation(beanName);
            // 2. 初始化 bean,委托给 ObjectFactory 完成 
            singletonObject = singletonFactory.getObject();           
            // 3. 从 singletonsCurrentlyInCreation 移除该 bean
            afterSingletonCreation(beanName);       
            // 4. 建立完成进行注册,这样下次就能够从缓存中直接获取这个对象了
            addSingleton(beanName, singletonObject);
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

(2) 方法2:单例对象建立完成进行注册源码分析

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);
    }
}

(3) 方法3:将实例化后的对象暴露到容器中this

Spring 在 bean 实例化后就会调用 addSingletonFactory 将这个对象提早暴露到容器中,这们就能够经过 getBean(A) 获得这个对象,即便这个对象仍正在建立。用于解决循环依赖。代理

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);
        }
    }
}

(4) 方法4:从缓存中获取 beancode

这个方法也是专门用于解决循环依赖的问题,当不存在循环依赖时 earlySingletonObjects 老是 null。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    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) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

2、Spring 建立 bean 过程

咱们从 BeanFactory#getBean(beanName) 调用提及,看一下这几个方法的调用顺序:

2.1 AbstractBeanFactory#doGetBean

这个方法先从缓存中获取 bean,没有再建立 bean,所以会调用方法 4 和方法 1,咱们看一下调用过程。

(1) getSingleton(beanName, true)

doGetBean 首先从缓存中获取数据,Object sharedInstance = getSingleton(beanName),这个方法最终会调用 getSingleton(beanName, true)

(2) getSingleton(beanName, singletonFactory)

若是缓存中没有 bean,则会调用 addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) 来建立一个新 bean,代码以下:

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

一旦调用 getSingleton(beanName, singletonFactory) 方法,这个方法建立开始时就会标记这个 bean 为正在建立,建立结束后移除对应的标记。直接建立 bean 的过程其实是委托给了 createBean 方法。继续跟踪这个方法。

2.2 AbstractAutowireCapableBeanFactory#doCreateBean

doCreateBean 方法中完成了单例的 bean 有如下几个主要的步骤:

  1. createBeanInstance 实例化 bean 对象,通常是经过反射调用默认的构造器。
  2. populateBean bean 属性注入,在这个步骤会从 Spring 容器中查找对应属性字段的值,解决循环依赖问题。
  3. initializeBean 调用的 bean 定义的初始化方法。

(3) addSingletonFactory(beanName, singletonFactory)

在 createBeanInstance 后 populateBean 前 Spring 会将这个实例化的 bean 提早暴露到容器中,这样 populateBean 属性注入时就能够经过 getBean(A) 查找到。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

(4) getSingleton(beanName, false)

在 bean 初始化完成还后还须要进行依赖的检查,这时由于提早暴露的这个 bean(即便用工厂方法或构造方法建立出来的对象) initializeBean 还能够进行加强,这样这两个 bean 就不是同一个了。Spring 默认是不容许这种状况发生的。

if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                        "Bean with name '" + beanName + "' has been injected into other beans [" +
                        StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                        "] in its raw version as part of a circular reference, but has eventually been " +
                        "wrapped. This means that said other beans do not use the final version of the " +
                        "bean. This is often the result of over-eager type matching - consider using " +
                        "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
        }
    }
}

(5) addSingleton(beanName, singletonObject)

在 bean 建立结束后还有一步是在 getSingleton(beanName, singletonFactory) 中完成的,调用 addSingleton(beanName, singletonObject),即注册最终的 bean,同时清空中间的辅助状态。

这样单例 bean 的建立过程就完成了,下面就须要分析循环引用下 singletonFactories、earlySingletonObjects 这两个集合的状态。

3、循环引用下 Bean 状态分析

3.1 正常状况

在正常的状况下,调用顺序以下:如下有无,表示是否持有对指定 Bean 的引用

过程 方法 singletonFactories earlySingletonObjects singletonObjects
缓存中获取 getSingleton(beanName, true)
建立 bean getSingleton(beanName, singletonFactory)
提早暴露到容器中 addSingletonFactory(beanName, singletonFactory)
依赖检查 getSingleton(beanName, false)
注册 addSingleton(beanName, singletonObject)

能够看到正常状况下,单例 bean 暴露的对象只会出如今 singletonFactories 集合中,不可能出如今 earlySingletonObjects 集合中,除非在建立 bean 的过程当中又调用了 getSingleton(beanName, true) 方法,也就是此时出现了循环引用。

3.2 循环引用

可是出现循环引用以后呢,就会出现这种状况:

过程 方法 singletonFactories earlySingletonObjects singletonObjects
缓存中获取 A getSingleton(A, true) A无B无 A无B无 A无B无
建立 A getSingleton(A, singletonFactory) A无B无 A无B无 A无B无
暴露 A 到容器中 addSingletonFactory(A, singletonFactory) A有B无 A无B无 A无B无
populateBean(A, mbd, instanceWrapper) A 注入 B 时又依赖了 A,此时由 B 准备解析 A……
缓存中获取 A getSingleton(A, true) A无B有 A有B无 A无B无
注册 B addSingleton(B, singletonObject) A无B无 A有B无 A无B有
populateBean(A, mbd, instanceWrapper) 完成属性注入
A- = initializeBean(beanName, exposedObject, mbd) 在 initializeBean 以后 A 变为 A-
依赖检查 getSingleton(beanName, false) A无B无 A有B无 A无B有
注册 A getSingleton(beanName, false) A无B无 A无B无 A有B有

在上面这个过程当中,在对 A 进行验证时,就会从 earlySingletonObjects 中取得一个 A,可是这个 A 和后面的 A- 可能不是同一个对象,这是由于有了 beanPostProcessor 存在,它能够改变 bean 的最终值,好比对原始 bean 进行封装,代理等。在这个过程当中,出现了 3 个对象 A, A-, B,而 B 中所持有的 A 对象为原始的 A。若是这里的 A 和 A- 不是同一个对象,即产生了 beanA 有了 beanB 的引用,但 beanB 并无 beanA 的引用,而是另外一个 beanA 的引用。


天天用心记录一点点。内容也许不重要,但习惯很重要!

相关文章
相关标签/搜索