网上关于spring循环依赖的介绍有不少,写的也很是好,文末的参考文章中列举了我看到的认为写的很不错的文章,此篇文章主要是一个总结性说明。本身也写了一个测试demo,详见 https://github.com/gsonkeno/spring-training/tree/master/springbean-circular-dependencyjava
先看一段构造器循环依赖的demo日志git
// 开始建立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正在建立中,直接抛出异常。关键代码github
public class DefaultSingletonBeanRegistry protected void beforeSingletonCreation(String beanName) { // 若是beanName当前正在处理中,说明遇到了环,直接抛异常 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } }
先看一段setter循环依赖(singleton)的demo日志,每行日志中头部不重要的信息已删除:web
// 开始建立单例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一遍理解的会更加清楚。spring
关键词:
解决bean的依赖前提早暴露bean的获取方式,建立中的bean进行缓存,递归深刻缓存
spring不支持原型bean属性注入循环依赖,不一样于构造器注入循环依赖会在建立spring容器context时报错,它会在用户执行代码如context.getBean()
时抛出异常。由于对于原型bean,spring容器只有在须要时才会实例化,初始化它。svg
由于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模式,就不会抛出异常。测试