Spring如何解析循环依赖

什么是循环引用?

比如对象A有属性B,对象B有属性A,这就是循环引用。

整个Spring Bean的创建分为:解析、注册、创建。循环引用则发生在bean的创建过程中。

废话不多上,直接上代码,以下是bean创建的核心代码:

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
	......
	......
	// 从缓存中取是否已经有被创建过的单态类型的Bean
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		// 获取bean实例的对象,如果是FactoryBean,则为bean实例本身或其创建的对象
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}else{
		......
		// 根据指定Bean名称获取合并的Bean定义,在Bean继承时 ,合并子类跟父类的公共属性  
		final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	    
	    .......
		// 创建单态模式Bean的实例对象 
		if (mbd.isSingleton()) {
			// 使用匿名内部类创建Bean实例对象,为什要用这样创建方式可以忽略,设计模式的一种 
			sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					try {
						// 创建Bean实例对象 
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						// 从容器中清除所有Bean缓存跟bean之间的依赖关系  
						destroySingleton(beanName);
						throw ex;
					}
				}
			});
			// 获取Bean的实例对象  
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
        ......
		......
	}
 
	return (T) bean;
}

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		synchronized (this.singletonObjects) {
			//从单例对象缓存中获取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				boolean newSingleton = false;
				try {
					//初始化bean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (Exception ex) {
					throw ex;
				}
				if (newSingleton) {
					//添加单例对象缓存
					addSingleton(beanName, singletonObject);
				}
			}
			return (singletonObject != NULL_OBJECT ? singletonObject : null);
		}
}

上面是创建bean的入口方法,逻辑很简单,先去容器中获取如果有,就直接返回,没有就创建。这里我们重点关注getSingleton(beanName)createBean(beanName, mbd, args)这两个方法。记住addSingleton(beanName, singletonObject);方法

这里可以先思考一个问题:
1.首先我们调用getSingleton(beanName)去获取,那么在什么情况下能获取到呢?

   / * 
     * @param beanName 要查找的bean的名称
	 * @param allowEarlyReference 是否允许创建早期引用(第一步获取的时候永远是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);
					if (singletonFactory != null) {
						//通过单例工厂获取单例对象
						singletonObject = singletonFactory.getObject();
						//添加到早期单例缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						//从单例工厂缓存删除
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

以下名词解释:

    // 单例对象的缓存: (简称一级缓存) 
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

	//  单例工厂缓存:(简称三级缓存)
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	//  早期单例对象缓存: (简称二级缓存) 
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

实际上真没有什么三级缓存,也没有真正顺序上的等级之分,这里只是为了简化记忆,包括在下文我会直接引用简称。

如果从程序正确执行的逻辑上来讲,这段代码先从一级缓存中获取,一级缓存没有从二级缓存中获取,二级没有从三级缓存中拿。如果三级缓存中有,将从三级缓存中获取实例对象,同时删除一级缓存,添加二级缓存。

这段代码会出现以下两种情形:

  1. 第一次创建,一级、二级、三级缓存都是空,所有才会执行接下来的createBean(beanName,mbd,args)方法进行创建。
  2. 第二次创建,一级、二级缓存为空,三级缓存不为空。只有在循环依赖的情况下才有第二次创建。

如果第二次创建成功(能成功解析循环引用),那么必定是从三级缓存中获取实例对象,也就是执行这块代码:

if (singletonFactory != null) {
	//通过单例工厂获取单例对象
	singletonObject = singletonFactory.getObject();
	//添加到早期单例缓存
	this.earlySingletonObjects.put(beanName, singletonObject);
	//从单例工厂缓存删除
	this.singletonFactories.remove(beanName);
}

以下有几个疑问?

  • 为什么第二次创建,一级、二级缓存为空,三级缓存不为空?
  • 为什么从三级缓存中获取bean实例后,要添加二级缓存,清除一级缓存呢?
  • 为什么说在第一次创建时,如果不添加三级缓存那么必定无法解析循环引用?
  • 在一次创建时,会在哪种情形下添加不了三级缓存呢?

记住:从三级缓存获取的bean实例的这行代码 singletonObject = singletonFactory.getObject(); 这也是解析循环引用的关键

带着以上几个疑问我们继续往下看,以下是createBean(beanName, mbd, args)方法

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
			throws BeanCreationException {
	......
	......
	// 忽略  aop动态代理就是从这里进入并返回一个代理对象(我们常用的aop切面功能就是这里实现的)
	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	if (bean != null) {
		return bean;
	}
 	......
 	// 创建bean的入口
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	return beanInstance;
}

以上我们只需关注doCreateBean(beanName, mbdToUse, args);这个方法,具体代码如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd,
			final Object[] args) throws BeanCreationException {
		
		// 创建实例对象
		BeanWrapper instanceWrapper =  createBeanInstance(beanName, mbd, args);
		// 获取创建的实例对象
		final Object bean = (instanceWrapper != null
				? instanceWrapper.getWrappedInstance()
				: null);
		.....
		.....
		// 忽略 调用PostProcessor后置处理器
		applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		.....
		.....

		// 是否曝光早期引用  条件是:单例 && 允许解析循环依赖 && 当前bean正在创建中
		boolean earlySingletonExposure = (mbd.isSingleton()
				&& this.allowCircularReferences
				&& isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			// 记住这个方法
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					// 以下这行代码可以理解 return bean;
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}
		Object exposedObject = bean;
		// 属性注入
		populateBean(beanName, mbd, instanceWrapper);
		if (exposedObject != null) {
			// 初始化Bean对象
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		.....
		.....
		return exposedObject;
}

doCreateBean()执行步骤如下:

  1. 创建实例 ,创建方式有空参、有参(自动装配)、工厂方法,一般使用无参构造。
  2. 添加缓存,将创建好的bean实例放入三级缓存中。
  3. 属性注入,给实例对象中的属性赋值。
  4. 初始化,在调用init-method属性指定的初始化方法前后作一些处理

下面我们主要看添加缓存的代码:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			//添加到单例工厂缓存
			this.singletonFactories.put(beanName, singletonFactory);
			//从早期单例对象缓存删除
			this.earlySingletonObjects.remove(beanName);
			//添加到已注册单例缓存
			this.registeredSingletons.add(beanName);
		}
	}
}

现在我们已经知道三级缓存是如何设置的了,注意:添加三级缓存是在实例化后,属性注入之前

  • 那么为什么要删除二级缓存呢?

答案就是在第二次创建时重新调用getSingleton(beanName)方法时,要想获取三级缓存,二级缓存必须清空。

  • 那还记得刚开始从三级缓存获取的那行代码么,singletonObject = singletonFactory.getObject(); 其中getObject()方法将获取到第一次创建的bean实例
	@Override
	public Object getObject() throws BeansException {
		// 以下这行代码可以理解 return bean;
		return getEarlyBeanReference(beanName, mbd, bean);
	}

当上面四个步骤都执行无误,也意味着createBean(beanName, mbd, args)方法执行完毕。
我们将再次回到文章最开始的这段代码:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		synchronized (this.singletonObjects) {
			//从单例对象缓存中获取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				boolean newSingleton = false;
				try {
					//初始化bean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (Exception ex) {
					throw ex;
				}
				if (newSingleton) {
					//添加单例对象缓存
					addSingleton(beanName, singletonObject);
				}
			}
			return (singletonObject != NULL_OBJECT ? singletonObject : null);
		}
}

添加一级缓存,清除二级、三级缓存

//添加单例对象缓存
addSingleton(beanName, singletonObject);

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

分析完了,循环引用就是这么解决的。
如果面试官问你为什么使用构造函数依赖注入不能解决循环引用,你就跟他说构造函数注入可以解决循环引用啊,告诉他将下面这两行代码注释就行了。(不过这样循环引用是解决了,但是注入了个寂寞,哈哈哈)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	.....
	//beforeSingletonCreation(beanName);
	.....
	//afterSingletonCreation(beanName);
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd,
			Object[] args) {
			.......
			/*if (ctors != null
				|| mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR
				|| mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}*/
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值