spring之循环依赖解决办法

(一)对象循环依赖

(1)循环依赖-->循环引用。--->即2个或以上bean 互相持有对方,最终形成闭环。

   eg:A依赖B,B依赖C,C又依赖A。【注意:这里不是函数的循环调用【是个死循环,除非有终结条件】,是对象相互依赖关系

spring之循环依赖解决办法

2.  循环依赖的场景?

①构造器依赖(这个解决不了,具体原因往下看)。

②通过setter循环依赖。(这个可以解决)。

3. 如何发现?

spring在创建对象之前(对象还未出现)的时候,会将放在一个Set容器里,beanName为key,表示该对象正在被创建中。

	/** Names of beans that are currently in creation */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

如果是构造器依赖,ABC构造器循环依赖,在创建A的时候singletonsCurrentlyInCreation中有A的beanName,然后发现需要B对象,先获取B,发现B不存在,那么继续创建B,先在singletonsCurrentlyInCreation中存放B的beanName,然后发现创建B对象的时候,需要先创建C,继续查询C,发现C不存在,那么现在singletonsCurrentlyInCreation中存放C的beanName,但是创建C的时候发现需要先创建A,获取A的时候spring发现singletonsCurrentlyInCreation存在A的beanName,那么表示A当前正在被创建中,spring抛出异常。

4.如何解决?

之前说到构造器依赖的时候,ABC创建失败的原因是因为A创建对象需要B,B创建对象需要C,C创建对象需要A,这个需求是在ABC都还未创建就彼此需要,彼此发现对象都不存在,所以才被抛出异常。

如但是通过setter的循环依赖,可以避免这个错误。

因为spring的创建分为3步。

spring之循环依赖解决办法

①createBeanInstance:初始化对象,类似于clazz.newInstance()

②populateBean:填充属性,bean的对象依赖属性就是在这一步进行填充

③initializeBean:调用spring配置中的init-method方法

经过上述三步,一个spring的bean才被完全创建。setter形式的循环依赖出现在第一步跟第二步之间,所以spring在存放bean的时候做了一点文章。

spring将单例bean的存放利用三个hashmap作为三个级别的缓存解决循环引用问题。

5.代码实现。

三级缓存源码主要是指:

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

这三级缓存分别指:

 singletonObjects:已经创建完成的单例对象的cache
 singletonFactories : 创建中的单例对象工厂的cache
 earlySingletonObjects :提前暴光的单例对象的Cache 。【与singletonFactories互斥】

首先,看如下代码:

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


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);
			}
		}
	}
  •  
  • (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));

bean对象填充属性的时候,先判断该bean对象是否为单例,并且是否允许提前暴露(一般都为true),如果条件都符合,则调用

 addSingletonFactory方法,将创建改bean对象的工厂类存存放到第三级缓存registeredSingletons中,然后如果创建过程中,该bean对象完整创建,那么该对象会被从registeredSingletons移除 然后加入到一级缓存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);
		}
	}

但是如果这个时候对象创建出现了循环引用,用之前的ABC对象举例。

A对象装配之前,先讲自己的对象引用存放三级缓存中registeredSingletons然后,然后发现需要对象B,调用getSingleton(B)方法,

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 != NULL_OBJECT ? singletonObject : null);
	}

获取B的过程,先去一级缓存中查询,如果没找到,再去二级缓存,三级缓存,这个时候因为B还没有创建,所以需要创建B对象,这个时候重复之前创建A的过程,B对象先将自己的对象引用存放到registeredSingletons中 然后,装配B对象,发现这个时候需要对象C,同理,先创建C。

注意!这个时候循环引用出现了,C的创建时需要装配A的,所以利索当然的调用getSingleton(A)方法,但是之前A虽然没有创建完全,不存在一级缓存singletonObjects,但是A的对象引用存在三级缓存registeredSingletons中,C获取到A之后,讲A从三级缓存registeredSingletons中删除,移到二级缓存earlySingletonObjects中,然后C创建完成,放置一级缓存singletonObjects中,B也创建完成,放置一级缓存singletonObjects中,随后A也创建完成,放置一级缓存singletonObjects中。

循环引用的问题就此解决。