Spring中的循环依赖

Spring&SpringBoot 专栏收录该内容
49 篇文章 0 订阅

  在前面 Spring Bean加载过程(二) 文章中,分析了AbstractBeanFactory#getBean()获取bean时,会调用到AbstractBeanFactory#doGetBean() ,在该方法中调用到AbstractAutowireCapableBeanFactory#createBean(),最终是调用了AbstractAutowireCapableBeanFactory#doCreateBean,这里面会清晰的看到创建Bean的几个过程:

  在调用AbstractBeanFactory#doGetBean()方法中会调用DefaultSingletonBeanRegistry#getSingleton()来看这个beanName是否已经加载并被DefaultSingletonBeanRegistry缓存了。
  
  DefaultSingletonBeanRegistry是共享bean实例的通用注册表,里面会缓存bean,主要关注类中的下面几个filed:

	/** 缓存singleton对象 */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

	/** 缓存制造singleton的工厂对象 */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** 缓存singletonFactory制造出来的singleton  */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

	/** 单例注册表 */
	private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);

  来看getSingleton(beanName)方法:

	@Override
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	
	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);
                    // 如果存在指定beanName的singletonFactory对象
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
                        // earlySingletonObjects存放singletonFactory产生的singleton  
						this.earlySingletonObjects.put(beanName, singletonObject);
                        //指定的beanName已被占用,需要在singletonFactories移除
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

  首先从singletonObjects中尝试获取,如果获取不到并且对象在创建中(singletonsCurrentlyInCreation中包含),则尝试从earlySingletonObjects中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则调用singletonFactory.getObject()获取。

  如果getSingleton(beanName)获取不到对应的bean,AbstractBeanFactory就开始创建:

				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "'beanName' must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				// ……
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<Exception>();
				}
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					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 != NULL_OBJECT ? singletonObject : null);
		}
	}

  这里的getSingleton方法大致流程是,从singletonObjects中获取名称为beanName的对象;如果为空,则beforeSingletonCreation()中先检查DefaultSingletonBeanRegistry 类中的inCreationCheckExclusions是否不包含该beanName,并在singletonsCurrentlyInCreation中添加该beanName,失败则抛出BeanCurrentlyInCreationException
  然后调用singletonFactory.getObject()即createBean()方法来创建该bean,创建成功后会从singletonsCurrentlyInCreation删除该beanName,并将beanName对应的对象添加到singletonObjects中:

	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

  
  注意,在AbstractAutowireCapableBeanFactory#doCreateBean()方法中,createBeanInstance()执行完成之后,此时单例对象此时已经被创建出来了,但是populateBean、initializeBean还没执行,但Spring此时也会将这个对象提前曝光出来:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		// ...
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
        // ...

		// 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, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
		// ...
    }
	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);
			}
		}
	}

  
  Spring这么设计有什么好处呢?来分析一下,假设Abean的某个field或者setter依赖了Bbean的实例对象,同时Bbean的某个field或者setter也依赖了Abean的实例对象,在这种循环依赖的情况下,会出现什么情况。
  Abean首先完成了初始化的第一步(createBeanInstance),并且将自己提前曝光到singletonFactories中,在进行初始化的第二步时,发现自己依赖对象Bbean,此时就尝试去getBean(BbeanName),发现Bbean还没有被创建,于是开始创建。Bbean在初始化第一步的时候发现自己依赖了对象Abean,于是尝试getBean(AbeanName),会先调用getSingleton(beanName)方法,在该方法中会在singletonFactories中拿到Abean对象,Bbean拿到Abean对象后顺利完全初始化之后将自己放入到singletonObjects中。此时返回Abean的初始化过程,Abean此时能拿到Bbean的对象顺利完成自己的初始化,最终Abean也完成了初始化,放入到singletonObjects中。整个过程没有问题。
  但是,如果是Abean的构造函数中依赖了Bbean的实例对象,同时Bbean的构造函数也依赖了Abean的实例对象,在这种循环依赖的情况下,Bbean在初始化时发现自己依赖了对象Abean,于是尝试getBean(AbeanName),调用getSingleton(beanName)方法发现没有,于是开始创建,而在singletonsCurrentlyInCreation已经有了Abean,于是抛出**BeanCurrentlyInCreationException**异常。

  以上是对singleton的bean分析,如果是prototype呢?由于Spring容器不进行缓存prototype对象,因此无法提前暴露一个创建中的Bean。所以prototype的循环依赖是存在问题的。
  
  总结:简单来说就是对象通过构造函数初始化之后就暴露到容器中,这样就不会存在循环初始化对象的情况了。


  构造循环依赖:这种情况,类A的构造方法中传入类B,同时类B的构造方法中传入类A,这种循环依赖会报错,Spring无法解决。
  字段循环依赖:这种循环依赖,Spirng可以解决并且不会报错。
  Setter注入:Setter注入和属性注入一致,可以解决。
  原型循环依赖:因为原型的Bean只有在使用的时候才会去创建,而且每次都是创建一个新的,因此无法解决。



为什么有三级缓存不是二级缓存?
  二级缓存可以解决这个问题吗?其实二级缓存是可以解决这个问题的,但是前面说了三级缓存的目的是将返回早起引用对象这个动作给用于预留一个扩展接口,在三级缓存中获取Bean调用的getEarlyBeanReference会回调SmartInstinationBeanPostProcessor这个接口的方法,开发者可以在这个阶段对返回的Bean对象做修改,如果AB循环依赖,B就会调用getEarlyBeanReference获取A,从而B会从三级缓存得到一个扩展后的A,并移动到二级缓存。
  换个角度假如只有2级缓存,那么用户扩展的动作就要放到二级缓存来做,这样每一次来获取的时候如果一级缓存没有都需要在二级缓存执行一次singletonFactory.getObject()触发一下获取Bean,这样这个方法很可能会被执行多次,每次都会创建一个新的对象,由此肯定会引起问题,引入了三级缓存singletonFactory.getObject()只会在第一次获取的时候,前两级缓存都不存在才会触发一次,并立刻将自己放到二级缓存,并清空三级缓存,后面每一次获取的都是这次创建的这个对象。
  如果不需要扩展,放进去的早期引用就是不完整的Bean,不考虑给其他后置处理器扩展,这样使用2级缓存是可以的。A直接将自己放入二级缓存,循环依赖的时候B直接从二级缓存获取A的早期引用保证B初始化成功再将自己加到一级缓存,然后A初始化成功后添加到一级缓存,但是这样就不能扩展了,因为这个扩展点很重要,在AOP的AnnotationAwareAspectJAutoProxyCreator就通过这个扩展点来保证代理对象的返回和代理对象的循环依赖问题解决(保证循环依赖的时候,返回的对象也是代理对象)
  综上来看三级缓存的目的是预留一共扩展接口,而这个接口和AOP有关。



解决循环依赖思路分析

先来分析为什么缓存能解决循环依赖。

上文分析得到,之所以产生循环依赖的问题,主要是:

A创建时—>需要B---->B去创建—>需要A,从而产生了循环

征服阿里P7的Spring循环依赖原理解析,高薪offer唾手可得

那么如何打破这个循环,加个中间人(缓存)

征服阿里P7的Spring循环依赖原理解析,高薪offer唾手可得

A的Bean在创建过程中,在进行依赖注入之前,先把A的原始Bean放入缓存(提早暴露,只要放到缓存了,其他Bean需要时就可以从缓存中拿了),放入缓存后,再进行依赖注入,此时A的Bean依赖了B的Bean,如果B的Bean不存在,则需要创建B的Bean,而创建B的Bean的过程和A一样,也是先创建一个B的原始对象,然后把B的原始对象提早暴露出来放入缓存中,然后在对B的原始对象进行依赖注入A,此时能从缓存中拿到A的原始对象(虽然是A的原始对象,还不是最终的Bean),B的原始对象依赖注入完了之后,B的生命周期结束,那么A的生命周期也能结束。

因为整个过程中,都只有一个A原始对象,所以对于B而言,就算在属性注入时,注入的是A原始对象,也没有关系,因为A原始对象在后续的生命周期中在堆中没有发生变化。

从上面这个分析过程中可以得出,只需要一个缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?

这是难点,基于上面的场景想一个问题:如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。

B依赖的A和最终的A不是同一个对象

那么如何解决这个问题?这个问题可以说没有办法解决。

因为在一个Bean的生命周期最后,Spring提供了BeanPostProcessor可以去对Bean进行加工,这个加工不仅仅只是能修改Bean的属性值,也可以替换掉当前Bean。

举个例子:

@Component
public class User {
}
@Component
public class LubanBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        // 注意这里,生成了一个新的User对象
        if (beanName.equals("user")) {
            System.out.println(bean);
            User user = new User();
            return user;
        }

        return bean;
    }
}
public class Test {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig.class);
        
        User user = context.getBean("user", User.class);
        System.out.println(user);

    }
}

运行main方法,得到的打印如下:

com.luban.service.User@5e025e70
com.luban.service.User@1b0375b3

所以在BeanPostProcessor中可以完全替换掉某个beanName对应的bean对象。

而BeanPostProcessor的执行在Bean的生命周期中是处于属性注入之后的,循环依赖是发生在属性注入过程中的,所以很有可能导致,注入给B对象的A对象和经历过完整生命周期之后的A对象,不是一个对象。这就是有问题的。

所以在这种情况下的循环依赖,Spring是解决不了的,因为在属性注入时,Spring也不知道A对象后续会经过哪些BeanPostProcessor以及会对A对象做什么处理

Spring到底解决了哪种情况下的循环依赖

虽然上面的情况可能发生,但是肯定发生得很少,我们通常在开发过程中,不会这样去做,但是,某个beanName对应的最终对象和原始对象不是一个对象却会经常出现,这就是AOP。

AOP就是通过一个BeanPostProcessor来实现的,这个BeanPostProcessor就是AnnotationAwareAspectJAutoProxyCreator,它的父类是AbstractAutoProxyCreator,而在Spring中AOP利用的要么是JDK动态代理,要么CGLib的动态代理,所以如果给一个类中的某个方法设置了切面,那么这个类最终就需要生成一个代理对象。

一般过程就是:A类—>生成一个普通对象–>属性注入–>基于切面生成一个代理对象–>把代理对象放入singletonObjects单例池中。

而AOP可以说是Spring中除开IOC的另外一大功能,而循环依赖又是属于IOC范畴的,所以这两大功能想要并存,Spring需要特殊处理。

如何处理的,就是利用了第三级缓存singletonFactories

首先,singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)

上面的Lambda表达式就是一个ObjectFactory,执行该Lambda表达式就会去执行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) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

该方法会去执行SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法,而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator,一个是InstantiationAwareBeanPostProcessorAdapter,它的实现如下:

// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    return bean;
}
// AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

所以很明显,在整个Spring中,默认就只有AbstractAutoProxyCreator真正意义上实现了getEarlyBeanReference方法,而该类就是用来进行AOP的。上文提到的AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator。

那么getEarlyBeanReference方法到底在干什么?

首先得到一个cachekey,cachekey就是beanName。

然后把beanName和bean(这是原始对象)存入earlyProxyReferences中

调用wrapIfNecessary进行AOP,得到一个代理对象。

那么,什么时候会调用getEarlyBeanReference方法呢?回到循环依赖的场景中

征服阿里P7的Spring循环依赖原理解析,高薪offer唾手可得

左边文字

这个ObjectFactory就是上文说的labmda表达式,中间有getEarlyBeanReference方法,注意存入singletonFactories时并不会执行lambda表达式,也就是不会执行getEarlyBeanReference方法

右边文字

从singletonFactories根据beanName得到一个ObjectFactory,然后执行ObjectFactory,也就是执行getEarlyBeanReference方法,此时会得到一个A原始对象经过AOP之后的代理对象,然后把该代理对象放入earlySingletonObjects中,注意此时并没有把代理对象放入singletonObjects中,那什么时候放入到singletonObjects中呢?

我们这个时候得来理解一下earlySingletonObjects的作用,此时,我们只得到了A原始对象的代理对象,这个对象还不完整,因为A原始对象还没有进行属性填充,所以此时不能直接把A的代理对象放入singletonObjects中,所以只能把代理对象放入earlySingletonObjects,假设现在有其他对象依赖了A,那么则可以从earlySingletonObjects中得到A原始对象的代理对象了,并且是A的同一个代理对象。

当B创建完了之后,A继续进行生命周期,而A在完成属性注入后,会按照它本身的逻辑去进行AOP,而此时我们知道A原始对象已经经历过了AOP,所以对于A本身而言,不会再去进行AOP了,那么怎么判断一个对象是否经历过了AOP呢?会利用上文提到的earlyProxyReferences,在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,会去判断当前beanName是否在earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。

对于A而言,进行了AOP的判断后,以及BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了,但是我们知道,应该是要A的代理对象放入singletonObjects中,所以此时需要从earlySingletonObjects中得到代理对象,然后入singletonObjects中。

整个循环依赖解决完毕。

总结

至此,总结一下三级缓存:

  1. singletonObjects:缓存某个beanName对应的经过了完整生命周期的bean
  2. earlySingletonObjects:缓存提前拿原始对象进行了AOP之后得到的代理对象,原始对象还没有进行属性注入和后续的BeanPostProcessor等生命周期
  3. singletonFactories:缓存的是一个ObjectFactory,主要用来去生成原始对象进行了AOP之后得到的代理对象,在每个Bean的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本bean,那么这个工厂无用,本bean按照自己的生命周期执行,执行完后直接把本bean放入singletonObjects中即可,如果出现了循环依赖依赖了本bean,则另外那个bean执行ObjectFactory提交得到一个AOP之后的代理对象(如果有AOP的话,如果无需AOP,则直接得到一个原始对象)。
  4. 其实还要一个缓存,就是earlyProxyReferences,它用来记录某个原始对象是否进行过AOP了。
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值