从源码解析Spring循环依赖问题

135 阅读8分钟

一、循环依赖问题

问题描述:A依赖B,B依赖A,形成闭环

问题产生场景一:构造器

@Service
public class A{
    private B b;
    
    public A(B b){
        this.b = b;
    }
}

@Service
public class B{
    private A a;
    
    public B(A a){
        this.a = a;
    }
}

项目启动失败:产生循环依赖

原因:

1、Spring根据构造器去实例化A

2、Spring去实例化A构造器所需的参数B

3、Spring根据构造器去实例化B

4、Spring去实例化B构造器所需的参数A

5、最后上面陷入死循环,导致项目启动失败。因为A未完成实例化,所以没把获取A的方法放入三级缓存,程序因此会陷入不断去实例化A和B的死循环。

image

问题产生场景二:属性注入

@Service
public class A{

    @Resource
    private B b;
}

@Service
public class B{

    @Resource
    private A a;
}

项目启动成功:spring解决循环依赖问题

image

二、Spring如何解决循环依赖问题

根本原因

其根本原因在于DefaultSingletonBeanRegistry类中的三级缓存

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

/** 二级缓存: 单例bean名称->单例bean对象,未初始化完成bean */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

/** 三级缓存: 单例bean名称->单例bean的创建方法 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三级缓存中的ObjectFactory是一个函数式接口,它最后指向的方法是getEarlyBeanReference(beanName, mbd, bean)

/**
 * 获取对指定 bean 的早期访问的引用,通常用于解析循环引用。
 * @param beanName bean 的名称(用于错误处理目的)
 * @param mbd bean 的合并 bean 定义
 * @param bean 原先的bean实例
 * @return 要公开为 bean 引用的对象
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	// 判断bean是否是我们自定义的,返回此工厂是否拥有将在创建时应用于单例 bean 的 InstantiationAwareBeanPostProcessor
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
			exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
		}
	}
	return exposedObject;
}

源码解析

项目代码如下:

@Service
public class A {

    @Resource
    private B b;
}

@Service
public class B {

    @Resource
    private A a;
}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

接下来开始在run方法上debug,这里直接跳到关键部分,同时代码太多直截取关键部分

第一部分:创建Bean

第一步:AbstractApplicationContext类的refresh()方法

// Instantiate all remaining (non-lazy-init) singletons.
// 这里实例化所有非懒加载的单例对象
finishBeanFactoryInitialization(beanFactory);

第二步:DefaultListableBeanFactory类的preInstantiateSingletons方法

// 这里会获取很多bean名称,包含我们需要实例化的bean名称,a和b
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 这里开始触发非懒加载的bean初始化
for (String beanName : beanNames) {
	...
    // 最后我们的bean会进入这里
	getBean(beanName);
}

第三步:AbstractBeanFactory的doGetBean方法

// 这里从三级缓存中获取当前bean,第一次A进来返回为null
Object sharedInstance = getSingleton(beanName);
......
// 这里创建A实例
sharedInstance = getSingleton(beanName, () -> {
	try {
		return createBean(beanName, mbd, args);
	}
	...
});
/**
 * 返回在给定名称下注册的(原始)单例对象。 
 * 检查已经实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)。
 * 这里就是从三级缓存中拿对象
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 快速检查没有完整单例锁的现有实例
	// 从一级缓存从获取
	Object singletonObject = this.singletonObjects.get(beanName);
	// 第一次A对象初始化进来这里时,直接结束,由于判断返回false,当前对象不在创建中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// 从二级缓存中获取
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							singletonObject = singletonFactory.getObject();
							this.earlySingletonObjects.put(beanName, singletonObject);
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}

第四步:DefaultSingletonBeanRegistry的getSingleton方法,获取bean实例

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	...
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null) {
		......
		// 这里会将当前bean名称放入,当前正在创建bean的Set集合中,与第三步isSingletonCurrentlyInCreation方法关联
		beforeSingletonCreation(beanName);
		boolean newSingleton = false;
		......
	    // 这里去获得真正的bean实例,从这里进入第五步
		singletonObject = singletonFactory.getObject();
		newSingleton = true;
	    ......
		// 这里会将当前bean名称从当前正在创建bean的Set集合中删除
		afterSingletonCreation(beanName);
		......
		// 将bean放入一级缓存,并将其从二级三级缓存中删除
		addSingleton(beanName, singletonObject);
	}
	return singletonObject;
}

第五步:AbstractAutowireCapableBeanFactory的createBean方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
	......
    // 去创建bean实例
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	......
}

第六步:AbstractAutowireCapableBeanFactory的doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
    ...
	// 这里开始去创建bean
	instanceWrapper = createBeanInstance(beanName, mbd, args);
	Object bean = instanceWrapper.getWrappedInstance();
	.......
	// 这里将bean放入三级缓存singletonFactories中
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	...
    // 填充属性,这里将把B填充进去
	populateBean(beanName, mbd, instanceWrapper);
	exposedObject = initializeBean(beanName, exposedObject, mbd);
	.......
	return exposedObject;
}

第七步:AbstractAutowireCapableBeanFactory的createBeanInstance这里创建了bean

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	......
	// 这里由于我们未定义任何构造器,直接进入最底下默认的初始化bean方法
	return instantiateBean(beanName, mbd);
}

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
	......
	// 这里创建了bean,我们看看里面有什么
	return BeanUtils.instantiateClass(constructorToUse);
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
	.......
	// 这里最终用反射创建了bean
	return ctor.newInstance(argsWithDefaultValues);
}

第二部分:初始化Bean

第八步:现在回到第六步中的addSingletonFactory方法,将bean放入三级缓存

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
		    // 当一级缓存不存在该bean的情况下
			if (!this.singletonObjects.containsKey(beanName)) {
			    // 将创建bean的方法放入三级缓存
				this.singletonFactories.put(beanName, singletonFactory);
				// 从二级缓存中删除当前bean
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

第九步:第六步中的populateBean方法,填充A的属性,我们直接进入到关键代码InjectionMetadata的inject方法

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)throws Throwable {
	if (this.isField) {
		Field field = (Field) this.member;
		ReflectionUtils.makeAccessible(field);
		// 这里把A的属性b填充进去,target就是A
		field.set(target, getResourceToInject(target, requestingBeanName));
	}
	......
}

第十步:这时候顺着第九步的getResourceToInject一直下去,你会发现又回到了第三步的doGetBean方法,不过这次get的是B,接下来就是把上面的九步再走了一遍,不过这次是B而已,这里就不再重复了。

最后,B同样也需要注入a,你会发现代码走下去,B也到了populateBean,这时候就是继续走下去,你会发现最后填充B中的属性的时候,去取A也走到了doGetBean方法,还记得第三步的getSingleton方法吗

/**
 * 返回在给定名称下注册的(原始)单例对象。 
 * 检查已经实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)。
 * 这里就是从三级缓存中拿对象
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 从一级缓存从获取
	Object singletonObject = this.singletonObjects.get(beanName);
	// 第一次A对象初始化进来这里时,直接结束,现在B来取A进入这里,由于前面A在第四步执行过beforeSingletonCreation方法,此时判断通过
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// 从二级缓存中获取
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// 从一级缓存从获取
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
				    // 从二级缓存中获取
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
					    // 从三级缓存中获取
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							singletonObject = singletonFactory.getObject();
							// 放入二级缓存
							this.earlySingletonObjects.put(beanName, singletonObject);
							// 删除三级缓存
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}

三级缓存中的ObjectFactory是一个函数式接口,它最后指向的方法是getEarlyBeanReference(beanName, mbd, bean)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
			exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
		}
	}
	return exposedObject;
}

这里他从三级缓存中获取bean,这时候循环依赖问题得到解决,直接从三级缓存中获取bean,然后放入二级缓存,删除三级缓存,返回bean。

第十一步:接下来B回到第四步,先是afterSingletonCreation将B从正在创建的bean的Set集合中删除,然后执行addSingleton

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
	    // 将B放入一级缓存
		this.singletonObjects.put(beanName, singletonObject);
		// 从三级缓存删除B
		this.singletonFactories.remove(beanName);
		// 从二级缓存删除B
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

第十二步:B创建结束并返回给A,然后A再重复第十一步,Bean实例化完成,循环依赖解决

三、思考

上述DEBUG过程中发现,其实二级缓存也可以解决循环依赖问题,直接将二级缓存和三级缓存合并,把bean放入三级缓存的代码改成放入二级缓存。

为什么要使用三级缓存

其实和AOP代理有关,我们改造下代码,在进行DEBUG。增加一个切面

@Aspect
@Component
public class DemoAspect {

    @Before(value = "execution(* com.example.demo.cycle.*.*(..))")
    public void before(){
        System.out.println("aop----before");
    }
}

其他地方都没变,在填充B的属性从三级缓存中拿A时,这时候返回将不在是简单的一个bean,而是一个代理类

// 注意这里开始,bean相当于分成两个,一个进入三级缓存,一个继续进行初始化
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

Object exposedObject = bean;
// 这里填充bean中的属性,从最开始A到这里->B到这里->B填充属性A,从三级缓存中获取A,这时给的是A的代理类,但最开始的A在这里还是一个普通的bean
populateBean(beanName, mbd, instanceWrapper);
// 这里从二级缓存中取出A的代理类
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
	if (exposedObject == bean) {
	    // 最后返回的是A的代理类
		exposedObject = earlySingletonReference;
	}

但是这里仔细查看你会发现,就算是为了代理类,还是能使用二级缓存实现,只要在一开始就将代理类放入二级缓存,也能实现,改造如下:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 直接将bean从三级缓存中拿出放入二级缓存,再从三级缓存中删除,相当于未使用三级缓存
getSingleton(beanName, true)

为方便快速验证我们的想法,使用IDEA的评估表达式(快捷键Alt+F8)直接在DEBUG中执行getSingleton方法来验证。

image

执行过后,项目同样正常启动无异常,所以二级缓存也可以解决循环依赖问题,那么为什么Spring还要使用三级缓存呢?

根本原因

这时候需要对比两种不同情况下,bean的完整创建过程。

改动源码前

存在AOP,无循环依赖问题:

1、构造器反射创建bean

2、populateBean方法填充属性

3、initializeBean方法初始化bean,在方法的最后(也就是bean生命周期的最后一步)获取应用的BeanPostProcessors列表,遍历执行其postProcessAfterInitialization方法,通过列表中的AnnotationAwareAspectJAutoProxyCreator创建bean的代理类

存在AOP,有循环依赖问题:

1、构造器反射创建bean

2、populateBean方法填充属性,使用到三级缓存创建bean代理类

3、initializeBean方法初始化bean

改动源码后

再看看我们改造后的代码过程有什么不同

存在AOP,有无循环依赖问题:

1、构造器反射创建bean

2、创建bean代理类放入二级缓存

2、populateBean方法填充属性

3、initializeBean方法初始化bean

总结

对比上述过程,你会发现改造后,不管存不存在循环依赖问题,都在bean填充属性和初始化之前创建了代理类,这不符合Spring的设计原则。而原本的代码,只有在发送循环依赖问题的情况下,没办法,才会在填充属性是创建代理类。Spring的设计之初就是在生命周期的最后一步完成代理