Spring循环依赖如何解决

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/gs_albb/article/details/85558641

网上关于spring循环依赖的介绍有很多,写的也非常好,文末的参考文章中列举了我看到的认为写的很不错的文章,此篇文章主要是一个总结性说明。自己也写了一个测试demo,详见 https://github.com/gsonkeno/spring-training/tree/master/springbean-circular-dependency

构造器循环依赖

先看一段构造器循环依赖的demo日志

// 开始创建bean staffA
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'studentA'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'studentA'

// 因为staffA的构造器方法参数依赖一个staffB,所以开始创建 staffB
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'studentB'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'studentB'

// 因为staffB的构造器方法参数依赖一个staffC,所以开始创建 staffC
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'studentC'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'studentC'

// 因为staffC的构造器方法参数依赖一个staffA,所以开始创建 staffA
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'studentA'

.support.AbstractApplicationContext 556 refresh - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentA' defined in file ...

因为创建staffA—>staffB—>staffC,再从staffC—>staffA的时候,spring检测到staffA正在创建中,直接抛出异常。关键代码

public class DefaultSingletonBeanRegistry
	protected void beforeSingletonCreation(String beanName) {
	    // 如果beanName当前正在处理中,说明遇到了环,直接抛异常
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
}

setter循环依赖(singleton)

先看一段setter循环依赖(singleton)的demo日志,每行日志中头部不重要的信息已删除:

// 开始创建单例bean(staffA)
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'staffA'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'staffA'

// 检测到staffA上的注入方法setStaffB
.annotation.InjectionMetadata 74 checkConfigMembers - Registered injected element on class [com.gsonkeno.circular.dependency.staff.StaffA]: AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffA.setStaffB(com.gsonkeno.circular.dependency.staff.StaffB)

// 过早地把staffA缓存起来,为可能出现的循环依赖提供解决办法
.support.AbstractAutowireCapableBeanFactory 563 doCreateBean - Eagerly caching bean 'staffA' to allow for resolving potential circular references

// 开始处理staffA上的注入方法setStaffB
.annotation.InjectionMetadata 88 inject - Processing injected element of bean 'staffA': AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffA.setStaffB(com.gsonkeno.circular.dependency.staff.StaffB)

// 开始创建单例staffB
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'staffB'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'staffB'

// 检测到staffB上的注入方法setStaffC
.annotation.InjectionMetadata 74 checkConfigMembers - Registered injected element on class [com.gsonkeno.circular.dependency.staff.StaffB]: AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffB.setStaffC(com.gsonkeno.circular.dependency.staff.StaffC)

// 过早地把staffB缓存起来,为可能出现的循环依赖提供解决办法
.support.AbstractAutowireCapableBeanFactory 563 doCreateBean - Eagerly caching bean 'staffB' to allow for resolving potential circular references

// 开始处理staffB上的注入方法setStaffC
.annotation.InjectionMetadata 88 inject - Processing injected element of bean 'staffB': AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffB.setStaffC(com.gsonkeno.circular.dependency.staff.StaffC)

// 开始创建单例staffC
.support.DefaultSingletonBeanRegistry 213 getSingleton - Creating shared instance of singleton bean 'staffC'
.support.AbstractAutowireCapableBeanFactory 460 createBean - Creating instance of bean 'staffC'

// 检测到staffC上的注入方法setStaffA
.annotation.InjectionMetadata 74 checkConfigMembers - Registered injected element on class [com.gsonkeno.circular.dependency.staff.StaffC]: AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffC.setStaffA(com.gsonkeno.circular.dependency.staff.StaffA)

// 过早地把staffC缓存起来,为可能出现的循环依赖提供解决办法
.support.AbstractAutowireCapableBeanFactory 563 doCreateBean - Eagerly caching bean 'staffC' to allow for resolving potential circular references

// 开始处理staffC上的注入方法setStaffA
.annotation.InjectionMetadata 88 inject - Processing injected element of bean 'staffC': AutowiredMethodElement for public void com.gsonkeno.circular.dependency.staff.StaffC.setStaffA(com.gsonkeno.circular.dependency.staff.StaffA)

// 从循环依赖环中,获取到了早早缓存的了单例staffA,尽管它还没有完全初始化
.support.AbstractBeanFactory 250 doGetBean - Returning eagerly cached instance of singleton bean 'staffA' that is not fully initialized yet - a consequence of a circular reference

// 装配staffC所依赖的staffA到staffC上去
.annotation.AutowiredAnnotationBeanPostProcessor 527 registerDependentBeans - Autowiring by type from bean name 'staffC' to bean named 'staffA'

// 结束创建staffC
.support.AbstractAutowireCapableBeanFactory 497 createBean - Finished creating instance of bean 'staffC'

// 装配staffB所依赖的StaffC到staffB上去
.annotation.AutowiredAnnotationBeanPostProcessor 527 registerDependentBeans - Autowiring by type from bean name 'staffB' to bean named 'staffC'

// 结束创建staffB
.support.AbstractAutowireCapableBeanFactory 497 createBean - Finished creating instance of bean 'staffB'

// 装配staffA所依赖的staffB到staffA上去
.annotation.AutowiredAnnotationBeanPostProcessor 527 registerDependentBeans - Autowiring by type from bean name 'staffA' to bean named 'staffB'

// 结束创建staffA
.support.AbstractAutowireCapableBeanFactory 497 createBean - Finished creating instance of bean 'staffA'
// 上面的日志是创建staffA时打出来的,可以认为staffB、staffC这两个bean是在创建staffA的过程中
// 被迫创建的,所以等到创建staffB、staffC时,由于已经创建过,直接从缓存中拿去即可
.support.AbstractBeanFactory 254 doGetBean - Returning cached instance of singleton bean 'staffB'
.support.AbstractBeanFactory 254 doGetBean - Returning cached instance of singleton bean 'staffC'

如何判断一个单例bean正在创建中呢? 主要在于singletonsCurrentlyInCreation,如下:

public class DefaultSingletonBeanRegistry{
	/** Names of beans that are currently in creation */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

在bean创建前(肯定在执行构造器方法前),会把它放进去,创建bean完成后,会放出来。主要逻辑已经在上面的日志中体现了出来,如果有兴趣,深入debug一遍理解的会更加清楚。

关键词:
解决bean的依赖前提前暴露bean的获取方式,创建中的bean进行缓存,递归深入

setter循环依赖(prototype)

spring不支持原型bean属性注入循环依赖,不同于构造器注入循环依赖会在创建spring容器context时报错,它会在用户执行代码如context.getBean()时抛出异常。因为对于原型bean,spring容器只有在需要时才会实例化,初始化它。

因为spring容器不缓存prototype类型的bean,使得无法提前暴露出一个创建中的bean。spring容器在获取prototype类型的bean时,如果因为环的存在,检测到当前线程已经正在处理该bean时,就会抛出异常。核心代码

public abstract class AbstractBeanFactory{
	/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<>("Prototype beans currently in creation");
			
	protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		// 如果beanName已经存在于正在处理中的prototype类型的bean集中,后面会抛出异常
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}
}

另外补充一句,三个bean中,只要有一个bean是singleton模式,就不会抛出异常。

参考

  1. Spring源码学习–Bean对象循环依赖问题解决(四)https://blog.csdn.net/qq924862077/article/details/73926268
  2. 知乎面试官会问关于spring的哪些问题 https://www.zhihu.com/question/39814046/answer/550590260

猜你喜欢

转载自blog.csdn.net/gs_albb/article/details/85558641