首先看一下下面的Spring配置文件java
<!-- beanA依赖于beanB -->
<bean id="beanA" class="top.okay3r.ClassA">
<property name="beanB" ref="beanB"/>
</bean>
<!-- beanB依赖于beanA -->
<bean id="beanB" class="top.okay3r.ClassB">
<property name="beanA" ref="beanA"/>
</bean>
复制代码
当IOC容器读取上面的配置时,就会先对beanA进行加载;在对beanA进行属性填充时,会发现beanA依赖于beanB,而后就会对beanB进行加载;当对beanB进行属性填充时,又会发现beanB依赖于beanA,因而就加载beanA... 能够想到,若是Spring的容器对于这种循环依赖问题不做出响应的处理,那么就会无限执行上面的过程。最终的结果就可能形成OOM从而致使程序崩溃 spring
咱们知道在Spring中,注入bean的方式有【构造器注入】和【setter注入】两种方式。但在咱们使用Spring管理bean时,可能会遇到一种特殊的状况,那么就是上面所说的循环依赖问题 咱们再看一下Spring建立bean的过程缓存
若是阅读过IOC相关的源码就会知道,建立bean的过程大致能够分为初始化bean
,对bean的属性进行填充
,对bean进行初始化
三个步骤app
init-method
方法,因此能够将一些初始化的行为写到这个方法中而后就来分析一下两种注入方式ui
在普通的java程序中,若是已经new出了一个对象,咱们就知道这个对象已是可用的了,不论它的属性是否完整。 但在Spring中,建立出来的bean必需要完成三个步骤才能被认为是可用的,才会将这个“完整”的bean放入到IOC容器中。 由于构造器注入是在实例化对象时反射调用构造器去注入参数,因此既然beanA、beanB的都拿不到完整的依赖,就会进行无限的循环调用,从而没法解决【循环依赖问题】。解决办法就只有是修改依赖关系了this
再看一下setter注入方式 setter注入方式就是new出一个对象后,调用该对象的set方法对属性进行赋值。此时对象已经被new出来了,只不过是不完整而已。 若是出现了循环依赖的问题,这就要比构造器注入的方式好的多 因此Spring对于循环依赖问题的解决就是针对于setter方法的spa
接下来就开始分析Spring是如何解决循环依赖问题的3d
首先咱们要知道,Spring对于循环依赖的问题是采用【缓存】的方式解决的 看一下Spring源码中的DefaultSingletonBeanRegistry类(注:SingletonBeanRegistry接口提供了关于访问单例bean的功能,DefaultSingletonBeanRegistry就是该接口的默认实现)代理
/** Cache of singleton objects: bean name to bean instance. */
// 用于存储完整的bean,接下来称之为【一级缓存】
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
// 用于存储不完整的bean,即只是new出来,并无属性值的bean,接下来称之为【二级缓存】
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
//用于存储bean工厂对象,接下来称之为【三级缓存】
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
复制代码
由于循环依赖都是产生在获取bean时,因此咱们直接从AbstractBeanFactory的getBean()方法开始code
Object sharedInstance = getSingleton(beanName);
上面并无涉及到循环依赖和二级、三级缓存的问题,由于对于循环依赖的处理,都表如今代码中的细节之处
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException{
// 从缓存中获取单例bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) { //若是获取到单例bean,则走下面代码
//......
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {//若是没有获取到单例bean,则走下面代码
//......
// 若是是单例的Bean,请下面的代码
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 建立单例Bean的主要方法,返回的bean是完整的
return createBean(beanName, mbd, args);
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//......
}
return (T) bean;
}
}
复制代码
上面的代码中,sharedInstance是经过getSingleton()方法得到的,实际上getSingleton(beanName)方法没什么逻辑,内部调用了getSingleton(beanName, boolean)这个方法,因此接下来就进入到这个方法中
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation : 判断当前单例bean是否正在建立中,也就是没有初始化完成。好比beanA的构造器依赖了beanB对象因此得先去建立B对象,或者在A的populateBean过程当中依赖了B对象,得先去建立B对象,这时的beanA就是处于建立中的状态
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存中获取单例bean
singletonObject = this.earlySingletonObjects.get(beanName);
// allowEarlyReference :是否容许从singletonFactories中经过getObject拿到对象
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存中获取单例bean
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 经过单例工厂获取单例bean
singletonObject = singletonFactory.getObject();
// 从三级缓存移动到了二级缓存,并移除singletonFactory
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
复制代码
从上面的代码中能够总结出如下几点:
singletonFactory.getObject()
)。经过ObjectFactory获取到的对象,是进行代理后的对象(假设有AOP)。将从【三级缓存】中获取到的对象放到【二级缓存】中,同时删除此beanName对应的【三级缓存数据】若是getSingleton()方法获取到了bean,即sharedInstance不为null,则对其进行处理而后返回 若是sharedInstance为null,就要走else中的代码了 首先判断一下是否为单例,(mbd是经过读取配置文件中bean标签生成的bean的定义信息,具体得到的方法这里不详细说了)。由于多例的bean是不须要放入到IOC容器中的,因此这里只处理单例bean 若是为单例,则调用getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
// ......
// 建立 bean 实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
if (newSingleton) {
// 添加新建立的bean添加到【一级缓存】中,并删除其余缓存中对应的bean
addSingleton(beanName, singletonObject);
}
// ......
// 返回 singletonObject
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将新建立的bean添加到【一级缓存】中
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
// 从其余缓存中移除相关的bean
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
复制代码
上面的代码主要包含了两个功能
能够看到bean实例是由singletonFactory.getObject()
拿到的,也就是经过doGetBean()
方法中判断是否单例后的匿名内部类获取到的,从而知道获取到的bean是由createBean()方法建立的
creatBean()方法调用了doCreatBean()方法,因此实际的建立逻辑就再doCreatBean()中
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// 默认调用无参构造实例化Bean
// 构造方法的依赖注入,就是发生在这一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 实例化后的Bean对象,这里获取到的是一个原始对象,即没有进行属性填充的对象
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
//......
// 解决循环依赖的关键步骤
// earlySingletonExposure:是否”提早暴露“原始对象的引用
// 由于不论这个bean是否完整,他先后的引用都是同样的,因此提早暴露的引用到后来也指向完整的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 若是须要提早暴露单例bean,则将该bean工厂放入【三级缓存】中
if (earlySingletonExposure) {
// 将刚建立的bean工厂放入三级缓存中singleFactories(key是beanName,value是FactoryBean)
// 一样也会移除【二级缓存】中对应的bean,即使没有
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//填充属性(依赖注入)
populateBean(beanName, mbd, instanceWrapper);
//调用初始化方法,完成bean的初始化操做
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//......
return exposedObject;
}
复制代码
ok,看到这里,整个在有循环依赖问题下建立、获取bean的流程就结束了 举个例子,从头串一下流程。假设beanA->beanB, beanB->beanA,即A、B相互依赖
整个过程大概就是这样了,因为spring的源码比较多,就只挑选了重点部分进行注释 其实主要思想就是利用二级、三级缓存对未初始化完成的bean进行提早的引用暴露,也就是将其设置为可引用的,这样当依赖于他的bean在进行属性填充时就能够直接拿到引用,解决了死循环的问题
还有几个比较重要的点,在这里指出位置,能够根据这些去查找看
》》》》》》》》》》》》》》》》》》》》》
有些东西不知道怎么转述成语言表达出来,还有若是有很差的或者说错的地方但愿看过的大佬能帮忙指正,谢谢~~