源码分析-spring是如何解决循环依赖的?

什么是循环依赖?

	Spring中的循环依赖指的是A类注入了B类属性,而B类又依赖了A类属性。如果有这种情况,那么在创建A对象
的时候,B还没有创建,如果去创建B,那A还没有创建。这种情况,Spring是如何解决的呢?

循环依赖的两种方式

同spring的依赖注入方式一样,setter方法和构造器方法。

setter方式

首先我们先用setter方式来表示这种循环依赖关系。两个bean:OrderService.java和UserService.java

@Component
public class OrderService {

	private static final Logger log = LoggerFactory.getLogger(OrderService.class);

	@Autowired
	private UserService userService;

}
@Component
public class UserService {

	private static final Logger log = LoggerFactory.getLogger(UserService.class);

	@Autowired
	private OrderService orderService;

}
创建OrderService对象

好,debug源码,在spring创建对象前,我们先看一下Spring容器中beanDefinitionMap中都有什么bean定义和顺序。
在这里插入图片描述
可以看到0-5是Spring内部的后置处理器和监听工厂等内置bean,6是我的配置类,以上不需要关注。7和8就是我们循环依赖的bean。按顺序我们就先关注orderService的实例化过程。
实例化的流程节点这里就不详细介绍了,这里主要着重研究的是如何解决循环引用,所以部分代码就略过了。
首先就是在实例化时的getSingleton方法内,它走了beforeSingletonCreation方法。他本质就是做个标记,表示此类正在被实例化,将此beanName添加到singletonsCurrentlyInCreation中,这里稍微有个印象即可。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				// 略过...
				beforeSingletonCreation(beanName);
				// 略过。。。
				
			}
			return singletonObject;
		}
	}
protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

紧接着跟源码就会到这里doCreateBean方法,此方法在spring执行中是比较重要的方法,它主要是实例化bean对象和初始化bean对象,实例化bean就是通过构造器去创建对象,初始化bean就是向此对象注入依赖。这里我们要关注的是实例化bean对象后的addSingletonFactory方法。
要执行addSingletonFactory方法,它的条件就是单例且bean工厂允许循环依赖(默认允许)和isSingletonCurrentlyInCreation(当前对象正在被创建,也就是之前提到的singletonsCurrentlyInCreation集合要包含此beanName)。

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

		// 实例化当前bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		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, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// 初始化bean
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
		// 略过...
		}
		// 略过...
		return exposedObject;
	}	

addSingletonFactory方法就是将此bean对象提前缓存起来。它的参数是beanName和ObjectFactory。spring采取的是lambda表达式来表示给定的ObjectFactory参数。
看这个方法内部,这里要提到spring的三种缓存,网上其它博客把它称为一/二/三级缓存,也可以,能方便理解些。

  • 一级缓存:singletonObjects-这里存放的是spring已经实例化且初始化完毕的bean对象。
  • 二级缓存:earlySingletonObjects-这里存放的是已经实例化但没有初始化的bean对象。
  • 三级缓存:singletonFactories-这里存放的是ObjectFactory对象,状态节点类似二级缓存,但本质上它ObjectFactory对象内部在调用getObject时会执行一个后置处理器,这里暂时不用管。

所以这里我们就知道了当前的半成品被封装成了ObjectFactory放入到了三级缓存singletonFactories中。

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);
			}
		}
	}
查找OrderService的依赖UserService,创建UserService对象

再往下就会执行populateBean方法,此方法会查找依赖,注入依赖。
首先就是查找依赖过程,由于我orderService类内部依赖了UserServiec,所以,它会在执行doResolveDependency中的findAutowireCandidates方法时,在容器内部匹配到同类型的UserService,后边就会通过getBean方法获取UserService对象。
在这里插入图片描述
这里开始我们就要研究它如何创建UserService了。
首先创建UserService的步骤同上边一样,也就是实例化bean后,将半成品对象封装成ObjectFactory也放入singletonFactories中,这里我们会发现singletonFactories就有了两个ObjectFactory对象。
在这里插入图片描述

查找UserService的依赖OrderService,再次创建获取OrderService对象

然后就会再次执行populateBean方法查找UserService它的依赖,当他查找匹配到OrderService时,就会再次执行getBean去获取OrderService对象,好,让我们跟着代码走到这里。是不是有点绕,又回到了当初的起点。但是不同的是在spring工厂内部肯定是维护了一些缓存,比如说上边的singletonFactories等。同时你要知道,到这里其实spring还没有完全初始化我们的bean对象,它仅仅是实例化而已。
在这里插入图片描述
好,进入doGetBean方法。这里主要关注的就是getSingleton方法。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		final String beanName = transformedBeanName(name);
		Object bean;

		// 尝试从缓存获取指定的bean对象
		Object sharedInstance = getSingleton(beanName);
		// 略过...
}			

注意这里,这里就会从缓存中获取OrderService这个bean对象,逻辑并不复杂。

  1. 首先会尝试从一级缓存singletonObjects中获取,前面我们说过,这里存的是spring完全初始化的对象,所以肯定是没有的。
  2. 然后就会尝试从二级缓存earlySingletonObjects中获取,这里我们之前都没有操作过,所以肯定没有。
  3. 再之后就会尝试从三级缓存singletonFactories中获取,因为我们之前走了两遍getBean,都实例化但没有完全初始化,所以这里就会有两个ObjectFactory对象。并且包含orderService对应的ObjectFactory。
  4. 紧接着它会执行getObject方法获取半成品对象。
  5. 获取到半成品orderService后,就会将此对象放入二级缓存earlySingletonObjects中,并且删除三级缓存中的对象。

在这里插入图片描述
执行到这,就会返回了我们UserService的依赖–OrderService对象,大功告成!!!
找到bean后,就会返回到UserService的初始化流程中,通过反射注入到UserService中。这样UserService就找到了所有依赖,进入到创建UserService对象的结尾操作。getSingleton方法结尾进行标记删除和重新缓存操作。

完成UserService的创建
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	// 略过

	// 创建对象完成,从singletonsCurrentlyInCreation删除此beanName。
	afterSingletonCreation(beanName);
	// 将bean对象添加到一级缓存
	if (newSingleton) {
		addSingleton(beanName, singletonObject);
	}
}

删除singletonsCurrentlyInCreation中的beanName,表示创建完毕。

protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

这里就会将此UserService对象缓存到一级缓存singletonObjects中,同时删除二级缓存earlySingletonObjects和三级缓存singletonFactories中的对象。

protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

好的,这里基本就结束了循环依赖的核心流程。后边就会返回UserService对象,又回到了创建OrderService的流程中,同上边一样,将返回的UserService对象通过反射注入到OrderService内。

总结setter方式的循环依赖解决思路

本质上,spring就是利用了java对象的地址引用特性。实例化OrderService后,在实例化UserService时,虽然OrderService还没有完全初始化,但可以先保留其地址引用,后续OrderService的任何初始化操作都不会影响地址引用的改变。所以Spring可以将其保存在三级或二级缓存内,进而可以解决循环依赖的问题。

构造器方式

代码如下
OrderService.java

@Component
public class OrderService {

	private static final Logger log = LoggerFactory.getLogger(OrderService.class);

	private UserService userService;

	@Autowired
	public OrderService(UserService userService) {
		this.userService = userService;
	}

}

UserService.java

@Component
public class UserService {

	private static final Logger log = LoggerFactory.getLogger(UserService.class);

	private OrderService orderService;

	@Autowired
	public UserService(OrderService orderService) {
		this.orderService = orderService;
	}

}

直接输出结论吧,构造器方式不支持循环依赖。他会爆出如下异常。

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1131)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1058)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:819)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:725)
	... 27 more
为什么构造器方式不支持循环依赖

首先还是先创建OrderService对象,但因为这里在构造器上添加了@Autowired注解,所以在spring实例化流程上,他会选择添加了@Autowired注解的构造器去实例化对象,然后会去解析这个构造器上的参数对象UserService。
这其中的过程与setter类似,在解析UserService时,又发现了它依赖OrderService对象,就会去再一次getBean这个OrderService对象。
不同的是这次再执行getSingleton(beanName)方法时,内部的三级缓存singletonFactories是没有OrderService的ObjectFactory的。所以,他后续会再一次执行创建bean的流程,但是在执行到下方代码beforeSingletonCreation方法时,就会产生异常。其实这个方法我们之前就注意过,功能是对当前创建的对象标记状态。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		
	// 略过
		
	// 对当前bean创建进行打标,标记状态--正在创建
	beforeSingletonCreation(beanName);

	// 略过
}

在这里插入图片描述
上面产生异常的原因就是singletonsCurrentlyInCreation已经包含了orderService,所以无法再次添加,也就意味着不允许再一次创建单例对象,不然就死循环了。

为什么spring不能仿造setter那样,在构造器执行时也放入三级缓存呢?
因为它不可能产生对象的,解析构造器参数就报错了,我们的bean的构造器还没有执行,又怎么会创建对应的对象!不可能加入三级缓存内。

总结

循环依赖大概就是以上的原理了,过程挺绕的,如果看代码,需要提前了解spring的IOC流程的,不然很容易深陷其中,最后放弃。setter方式可以解决循环依赖,构造器方式是无法解决的。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值