欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring中是如何处理循环依赖的

程序员文章站 2022-06-01 13:10:47
...

扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,阅读更多Spring源码分析文章。

Spring中是如何处理循环依赖的

IOC模块可以算得上是Spring中最核心的功能了,它完成了Bean的自动装配。在我们的业务代码中,经常会出现A类的实例中需要注入B类的实例,B类的实例需要注入A类的实例,这种情况我们称之为循环依赖。按照我们通常的想法,这个时候就应该出现死循环了,无法完成依赖注入过程。而在实际项目中,在项目启动时系统并没有报错,这是为什么呢?答案就是Spring在自动装配的过程中为我们处理了循环依赖的这种情况。那么Spring是如何处理循环依赖的问题的呢?

循环依赖

  • 首先我们定义如下两个类:UserService和OrderService,并让它们相互依赖。
@Service
public class UserService {

	@Autowired
	private OrderService orderService;

	public void query(){
		System.out.println(orderService);
	}
}
@Service
public class OrderService {

	@Autowired
	private UserService userService;

	public void query(){
		System.out.println(userService);
	}
}

在分析Spring如何解决循环依赖之前,建议读者先看下Spring的refresh()方法的流程和Bean的创建过程,可以参考笔者的另外两篇文章。通过源码看Bean的创建过程Spring源码系列之容器启动流程

    1. Spring在启动时,会在refresh()方法中创建所有的单例Bean,并完成自动装配。对于上面我们定义的两个类,假如先创建OrderService,那么就会先调用getBean(orderService),getBean()方法会调用到doGetBean()。
    1. 在doGetBean()方法中会先调用getSingleton(orderService)方法,判断它的返回值是否为空(此时第一次调用,返回值一定为空),如果不为空,则返回bean,如果为空,则继续执行下面的逻辑。在后面又会调用到getSingleton()的一个重载方法:getSingleton(orderService,lambda表达式),在这个方法中会调用createBean()方法。doGetBean()简化后的代码如下:
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;
	// 调用getSingleton(),第一次调用的时候,返回值肯定为空,因为此时orderService还没有被创建
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}else {
		try {
			if (mbd.isSingleton()) {
				// 调用getSingleton()的重载方法,第二个参数是一个lambda表达式
				sharedInstance = getSingleton(beanName, () -> {
					try {
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						throw ex;
					}
				});
			}
		}
		catch (BeansException ex) {
			throw ex;
		}
	}
	return (T) bean;
}
    1. createBean()方法会调用到doCreateBean()方法,在doCreateBean()方法中,会先实例化Bean(此时的Bean是一个半成品),然后将Bean通过addSingletonFactory()方法放入到singletonFactories属性中,接着调用populateBean()方法为Bean填充属性,完成自动装配。例如:先创建orderService,然后将其放到singletonFactories中,接着通过populateBean()方法为orderService装配userService属性。
    1. 在为orderService装配userService属性时,由于userService此时还没有被创建,所以又会调用到getBean(userService)。既然调用到getBean()方法,那么就会同上面的逻辑一样,先调用getSingleton(userService)判断是否为空,此时仍然为空,因为是第一次获取userService,所以接着会调用到createBean()、doCreateBean()。
    1. 当进入到doCreateBean()当中时,同样也是先实例化userService,然后将半成品的userService放入到singletonFactories中,接着通过populateBean()方法来为userService装配orderService属性。
    1. 在为userService装配orderService属性时,又会调用到getBean(orderService)方法,然后调用doGetBean(orderService)。接下来一步就与之前不一样了,在doGetBean(orderService)中仍然是先调用getSingleton(orderService)方法,此时该方法不会返回null了,因为在第3步中,将orderService放入到了singletonFactories中,所以此时返回值不为空,那么doGetBean()方法就会将bean返回,不会再调用到createBean()方法去创建orderService了。getSingleton()方法的源码如下:
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 先从singletonObjects获取
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			// 然后从earlySingletonObjects中获取
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				// 最后从singletonFactories中获取
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}
    1. 可以看到在getSingleton(beanName,allowEarlyReference)中,会先从singletonObjects中获取bean,如果获取不到,就从earlySingletonObjects中获取,如果还获取不到,就从singletonFactories中获取,所以当第一次调用getSingleton(beanName,allowEarlyReference)时,肯定返回null,只有当将bean放入到singletonFactories中了,才会不为空。由于在创建orderService时,就已经将orderService放入到singletonFactories中了,所以此时会将orderService返回。
    1. 当将orderService对象返回后,代码就会回到userService对象的populateBean()方法中,此时就能将其赋值给userService对象中的orderService属性了。(注意此时的orderService对象仍然是一个半成品,这个对象中的属性还没有被填充,但是没有关系,因为userService对象持有的是orderService的引用,所以当后面orderService的属性变化了,对userService是没有影响的。)
    1. 接着进行userService对象的其他属性填充,当userService完全初始化完成后,先将userService对象放入到singletonObjects中,再将userService对象返回。然后代码就会回到orderService对象的populateBean()方法处了,此时就可以将userService对象赋值给orderService对象的userService属性了,接着继续完成orderService的初始化操作。最后将orderService返回,然后将其放入到singletonObjects中。这样就完成了对象之前循环依赖。
  • 整体流程可以参考如下流程图:
    Spring中是如何处理循环依赖的
  • 从上面的分析中可以发现,解决循环依赖的主要功臣是getSingleton(beanName)、addSingletonFactory()这两个方法和singletonFactories、earlySingletonObjects、singletonObjects这三个属性。在Bean实例化之后属性填充之前,先将Bean通过addSingletonFactory()方法放入到singletonFactories中,当出现循环依赖时,先通过getSingleton(beanName)依次从singletonObjects、earlySingletonObjects、singletonFactories这三个属性中获取Bean,如果bean存在,则返回。由于是对象引用,所以即使早期装配的是半成品的Bean,也没有影响。

总结

  • 本文通过举例UserService和OrderService,分析了Spring解决循环依赖的过程,最后通过一张流程图进行了总结。
  • 关于singletonFactories、earlySingletonObjects、singletonObjects这三个属性,最后补充说明一下,singletonFactories中存放的bean是通过构造器反射创建出来的bean,没有进行任何属性填充,是个半成品bean;earlySingletonObjects中存放的也是半成品,只不过在singletonFactories的基础上多了一次Bean后置处理器的处理;singletonObjects中存放的是最后完整的Bean,我们通常所说的Spring的Bean容器,可以简单理解为就是singletonObjects。
  • 关于本文中提到的Spring容器的refresh()方法以及getBean()、createBean()方法,详细的源码分析可以参考笔者的另外两篇文章。
  • 通过源码看Bean的创建过程
  • Spring源码系列之容器启动流程

相关推荐

Spring中是如何处理循环依赖的