关于spring:spring是如何解决循环依赖的

34次阅读

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

首先,须要明确的是 spring 对循环依赖的解决有三种状况:

①结构器的循环依赖:这种依赖 spring 是解决不了的,直 接抛出 BeanCurrentlylnCreationException 异样。
②单例模式下的 setter 循环依赖:通过“三级缓存”解决循环依赖。
③非单例循环依赖:无奈解决。

spring 单例对象的初始化大略分为三步:

createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
populateBean:填充属性,这一步次要是多 bean 的依赖属性进行填充
initializeBean:调用 spring xml 中的 init 办法。

从下面讲述的单例 bean 初始化步骤咱们能够晓得,循环依赖次要产生在第一、第二步。也就是结构器循环依赖和 field 循环依赖。

接下来,咱们具体看看 spring 是如何解决三种循环依赖的。

1、结构器循环依赖

this .singletonsCurrentlylnCreation.add(beanName)将以后正要创立的 bean 记录在缓存中
Spring 容器将每一个正在创立的 bean 标识符放在一个“以后创立 bean 池”中,bean 标识
柏:在创立过程中将始终放弃在这个池中,因而如果在创立 bean 过程中发现自己曾经在“以后
创立 bean 池”里时,将抛出 BeanCurrentlylnCreationException 异样示意循环依赖;而对于创立结束的 bean 将从“以后创立 bean 池”中革除掉。

2、setter 循环依赖

Spring 为了解决单例的循环依赖问题,应用了三级缓存。

/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);

这三级缓存的作用别离是:
singletonFactories:进入实例化阶段的单例对象工厂的 cache(三级缓存)
earlySingletonObjects:实现实例化然而尚未初始化的,提前暴光的单例对象的 Cache(二级缓存)
singletonObjects:实现初始化的单例对象的 cache(一级缓存)
咱们在创立 bean 的时候,会首先从 cache 中获取这个 bean,这个缓存就是 sigletonObjects。次要的调用办法是:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation()判断以后单例 bean 是否正在创立中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference 是否容许从 singletonFactories 中通过 getObject 拿到对象
            if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {singletonObject = singletonFactory.getObject();
                    // 从 singletonFactories 中移除,并放入 earlySingletonObjects 中。// 其实也就是从三级缓存挪动到了二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

从下面三级缓存的剖析,咱们能够晓得,Spring 解决循环依赖的窍门就在于 singletonFactories 这个三级 cache。这个 cache 的类型是 ObjectFactory,定义如下:

public interface ObjectFactory<T> {T getObject() throws BeansException;
}

这个接口在 AbstractBeanFactory 里实现,并在外围办法 doCreateBean()援用上面的办法:

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

这段代码产生在 createBeanInstance 之后,populateBean()之前,也就是说单例对象此时曾经被创立进去(调用了结构器)。这个对象曾经被生产进去了,java 培训此时将这个对象提前曝光进去,让大家应用。

这样做有什么益处呢?让咱们来剖析一下“A 的某个 field 或者 setter 依赖了 B 的实例对象,同时 B 的某个 field 或者 setter 依赖了 A 的实例对象”这种循环依赖的状况。A 首先实现了初始化的第一步,并且将本人提前曝光到 singletonFactories 中,此时进行初始化的第二步,发现自己依赖对象 B,此时就尝试去 get(B),发现 B 还没有被 create,所以走 create 流程,B 在初始化第一步的时候发现自己依赖了对象 A,于是尝试 get(A),尝试一级缓存 singletonObjects(必定没有,因为 A 还没初始化齐全),尝试二级缓存 earlySingletonObjects(也没有),尝试三级缓存 singletonFactories,因为 A 通过 ObjectFactory 将本人提前曝光了,所以 B 可能通过 ObjectFactory.getObject 拿到 A 对象(尽管 A 还没有初始化齐全,然而总比没有好呀),B 拿到 A 对象后顺利完成了初始化阶段 1、2、3,齐全初始化之后将本人放入到一级缓存 singletonObjects 中。此时返回 A 中,A 此时能拿到 B 的对象顺利完成本人的初始化阶段 2、3,最终 A 也实现了初始化,进去了一级缓存 singletonObjects 中,而且更加侥幸的是,因为 B 拿到了 A 的对象援用,所以 B 当初 hold 住的 A 对象实现了初始化。

3、非单例循环依赖

对于“prototype”作用域 bean, Spring 容器无奈实现依赖注入,因为 Spring 容器不进行缓
存“prototype”作用域的 bean,因而无奈提前裸露一个创立中的 bean。

正文完
 0