循环依赖的处理流程

警告
本文最后更新于 2020-11-27,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


1 什么是循环依赖

在我们日常开发中,肯定存在这种情况:bean A 某个成员是 bean B,bean B 中某个属性是 bean A。那A类和B类就是相互依赖的关系,也叫循环依赖。

当然,也可以是这种情况 A -> B -> C -> D -> E -> A。这种关系也是循环依赖。

2 循环依赖在spring中的实现原理

1
2
3
4
5
6
7
8
9
@Service
public class CyclicGoodsServiceImpl implements CyclicGoodsService {
	@Autowired
	private CyclicOrderService cyclicOrderService;
	@Override
	public CyclicOrderService getCyclicOrderService() {
		return cyclicOrderService;
	}
}

2.1 源码分析

在这之前,先看一个简单的循环依赖代码案例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
public class CyclicGoodsServiceImpl implements CyclicGoodsService {
	@Autowired
	private CyclicOrderService cyclicOrderService;

	@Override
	public CyclicOrderService getCyclicOrderService() {
		return cyclicOrderService;
	}
}

@Service
public class CyclicOrderServiceImpl implements CyclicOrderService {
	@Autowired
	private CyclicGoodsService cyclicGoodsService;

	@Override
	public CyclicGoodsService getCyclicGoodsService() {
		return cyclicGoodsService;
	}
}

@Slf4j
public class ApplicationContextTest {
    @Test
    public void testCyclicDependence(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("top.wlz922.cyclic");
        CyclicGoodsService goodsService = context.getBean(CyclicGoodsService.class);
        CyclicOrderService orderService = context.getBean(CyclicOrderService.class);
        log.debug(goodsService.toString());
        log.debug(orderService.toString());
    }
}
只是简单的打印一下两个Service类中属性的值,很明显是注入成功了。

以下是打印的内容:

1
2
19:34:42.334 [main] DEBUG top.wlz922.test.context.ApplicationContextTest 36 - top.wlz922.cyclic.CyclicGoodsServiceImpl@7fa98a66
19:34:42.335 [main] DEBUG top.wlz922.test.context.ApplicationContextTest 37 - top.wlz922.cyclic.CyclicOrderServiceImpl@15ff3e9e

前面的文章中已经分析过,spring中获取bean实例的方法是getBean()。在容器启动的时候,会调用这些方法,实例化单例bean实例。

直接来到doGetBean(),看以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

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

		//转换beanName,这里传入进来的beanName可能是以&开头的,这里会截掉&
		final String beanName = transformedBeanName(name);
		Object bean;

		/*
		TODO 这里多级缓存用于解决循环依赖问题。
			注:循环依赖只能是属性见的依赖,不能是构造函数中参数的循环依赖。
		    从缓存(一级二级三级缓存依次获取,获取到就返回)中获取单例实例,如果获取到并且是无参构造函数,则将bean变量的值赋值。
		 */
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			// 省略无关代码
			// 如果bean不是FactoryBean类型或者beanName以&开头,则直接返回。
			// 否则将bean强转为FactoryBean类型,并调用getObject()方法返回。
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		} 
		// 省略无关代码...
		return (T) bean;
	}
}

先跳过无关代码,看关键点。从上面代码中可以看出,在doGetBean()中先调用了getSingleton(),如果这个值不为空就调用getObjectForBeanInstance(),经过一定的处理(先不要关心具体处理了什么),返回这个bean实例。

可以先分析,从接收变量的变量名sharedInstance就可以看出来,这是一个bean实例。关键点就在getSingleton()方法,这里可能会获取到一个bean实例。

下面是 getSingleton() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /** 一级缓存 里面存储的是bean实例,在bean实例化完成、依赖注入、代理生成等流程全部完成之后,会存储到这个容器中 */
    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** 
     * 三级缓存,在单例bean刚刚创建完成时,会将其封装成一个ObjectFactory对象,存储到这个容器中。键值是beanName。
     * 从源码中可以看到,实际上是提供了一些通过BeanPostProcessor进行的扩展操作。这里不会执行,只是把这个扩展操作流程封装起来。
     * 实际上,这里封装的一个函数式接口,在这里可以通过BeanPostProcessor进行一些扩展操作,其节点是bean实例化之后,
     * 在真正getBean时,会触发调用。 */
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /** 
     * 二级缓存 从三级缓存获取后,会将bean实例存储到二级缓存,并从三级缓存中移除。这里封装了一个函数式接口的匿名实例,
     *  依赖注入过程中通过getBean方法获取这个实例时,可以对其进行一些指定操作,比如说aop代理对象生成,就可能在这里触发。
     */
    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		/**
		 * 从一级缓存中取,如果有值,则返回。否则依次查找二级、三级缓存,如果最终从三级缓存中拿到值,
		 * 则将bean对象升级到二级缓存,并把原值从三级缓存中移除。
 		 */
		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;
	}
}
从源码添加的中文注释中可以看出,BeanFactory有这么三个关键成员。分别是一、二、三级缓存。

一级缓存 singletonObjects:封装了最终完成了所有处理流程的Bean。
二级缓存 earlySingletonObjects:封装了通过ObjectFactory.getObject()处理过后的对象。
三级缓存 singletonFactories:Bean刚刚创建完实例后,将Bean实例封装成ObjectFactory对象,存储到这里。

暂且记着这三个缓存的作用,来找bean被存储到三级缓存的源头。

在一个bean刚刚创建的时候,会调用到doCreateBean方法,在这个方法中真正实例化了bean,那么实例化完成之后,紧接着就会将其包装成ObjectFactory对象,存储到三级缓存。来到这块代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    			throws BeanCreationException {
		// 省略无关代码...
        // 创建bean实例
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        // 省略代码:对一些注解支持的扫描、合并到BeanDefinition...
        // 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.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            // 提前暴露bean实例的引用,这时候的bean是刚刚创建完成的,还没有进行属性的依赖注入、等流程处理。
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
    }
}
从上面列出的spring源码中可以看到,当一个bean创建完实例后,会调用addSingletonFactory(),这个方法的第二个参数,传递了一个匿名对象,这个对象就是ObjectFactory类型的。

来到addSingletonFactory()源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				// 将这个bean的匿名对象,存储到三级缓存。匿名对象中封装了一个扩展点的操作。
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}
}
可以看到,在bean刚刚创建完成,就把其封装到了ObjectFactory中,存储到了三级缓存singletonFactories(一个Map)。

从调用这个方法的地方(addSingletonFactory()),可以看到,这个匿名函数的内容:() -> getEarlyBeanReference(beanName, mbd, bean)

来看一下这个匿名对象具体做了什么:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		// 获取早期的Bean对象的引用,这时候的Bean还没有完成依赖注入及其后面的流程。
		// 在这里获取引用时,支持一些扩展操作,其扩展内容是通过 getEarlyBeanReference() 实现的。
		// getEarlyBeanReference() 来自 BeanPostProcessor 的子接口 SmartInstantiationAwareBeanPostProcessor。
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
}
从源码中可以看出:在返回早期暴露的Bean实例引用之前,会通过 BeanPostProcessor 进行一些处理。

处理内容:

摘要
  1. 获取到容器中所有已注册的BeanPostProcessor。
  2. 判断是否是其子接口SmartInstantiationAwareBeanPostProcessor类型,如果是,强转。
  3. 调用 getEarlyBeanReference() 获取Bean提前暴露对象的引用,在这里可以对Bean实例做一些扩展操作(比如生成代理对象等)。

在addSingletonFactory(),可以看到,这里只是将扩展操作封装起来,放入三级缓存,并没有调用它。

回到文章开头部分,依次从一、二、三级缓存中获取实例,在执行三级缓存获取操作时,就会调用这个方法,拿到Bean提前暴露的引用。拿到之后从三级缓存中移除,放入二级缓存。然后返回。

这也解释了为什么多个实例之间相互依赖,spring依然能够将依赖实例注入到对应成员中。

还有一个点,在哪里放入到一级缓存的?前面的文章中,有列出创建bean实例的入口,createBean方法是在一个函数式接口方法中调用的,它的上层方法是getSingleton()。

来到getSingleton()源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			// 省略无关代码...	
            //这里调用了外层传进来的匿名内部类,lambda表达式。
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
				
            // 省略无关代码...
            // 加入一级缓存
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
		}
		return singletonObject;
	}
}
singletonFactory.getObject() 中调用了createBean,这里完成了bean的实例化、依赖注入、代理生成等等操作。

在createBean所有操作完成之后,最后调用了addSingleton()方法。这里把bean实例加入到了一级缓存,并从二级缓存中移除。

来到addSingleton()源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    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);
		}
	}
}
很简单的逻辑,只是把一个对象放入到一级缓存,并从二级、三级缓存中移除。最后一个是已注册的单例bean容器,暂不讨论。

2.2 源码中要注意的细节

注意

构造函数中的依赖注入不可以循环依赖,多例依赖注入也不可以循环依赖。

  • 构造函数不允许循环依赖:很容易明白,如果想要创建一个对象,比如调用它的构造函数,即使反射调用也是如此。如果A构造函数依赖B,B构造函数中依赖A,那么构造函数就无法调用完成,对象也就无从创建。永远也拿不到提前暴露的对象引用。

  • 多例情况:不允许循环依赖,由于是多例,每次getBean都会创建新的实例,假如代码中定义了有循环依赖的属性,如果被允许,就会陷入死循环。

多例情况,如果有循环依赖,会抛出异常,源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                                  @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        // 多例不支持循环引用。多例时,如果返回指定的原型bean是否正在创建中,则抛出异常。
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }
}

构造函数中如果有循环依赖,源码ConstructorResolver类中有这段代码,抛出异常:

1
2
3
4
5
6
try {
    // 省略无关代码,如果这里有构造函数中的循环依赖,则抛出异常
}catch (BeansException ex) {
    throw new UnsatisfiedDependencyException(
            mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
}

2.3 流程梳理

例如有A、B两个类进行实例化,分别将对方的实例注入到自身成员中,代码如下(前文中类似案例):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Service
public class A {
	@Autowired
	private B b;
}

@Service
public class B {
	@Autowired
	private A a;
}

分析上面代码的执行流程:

  1. A 类无参构造函数实例化后, 设置三级缓存。
  2. A 类 populateBean 进行依赖注入, 这里触发了属性 B 的 getBean 操作。
  3. B 类无参构造函数实例化后,设置三级缓存。
  4. B 类 populateBean 进行依赖注入,这里触发了属性 A 的 getBean 操作
  5. A 类之前正在实例化, singletonsCurrentlyInCreation 集合中有已经有这个 A 实例了,三级缓存里面也有了,所以这时候是从三级缓存中拿到的提前暴露的 A 实例,该实例还没有进行属性 B 的依赖注入,属性 B 为空。
  6. B 类拿到了 A 的提前暴露实例注入到属性 A 中了。
  7. B 类实例化已经完成,B 类的实例化是由 A 类实例化中属性 B 的依赖注入触发的 getBean 操作进行的,现在 B 已经实例化,所以 A 类中属性 B 就可以完成依赖注入了,这时候 A 类 B 属性已经有值了。
  8. B 类 A 属性指向的就是 A 类实例堆空间,所以这时候 B 类 A 属性也会有值了。