在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出个人代码示例java
1 @Component 2 public class AddressVO { 3 4 @Autowired 5 private UserVO userVO; 6 } 7 8 9 @Component 10 public class UserVO { 11 12 @Autowired 13 private AddressVO addressVO; 14 15 public void test(){ 16 System.out.println(addressVO.getClass()); 17 } 18 } 19 20 @Configuration 21 @ComponentScan("com.springsource.study.reference") 22 public class AppConfig { 23 }
前提是,bean都是单实例的(singleton),咱们下面在学习源码的时候,也默认当前bean是单实例的;git
1.首先说,循环依赖的处理是在refresh()方法中,finishBeanFactoryInitialization(beanFactory);这个方法中完成的;这个方法的做用是:在spring将全部的bean添加到beanDefinitionMap以后,实例化全部的bean;github
先说明spring在解决循环依赖所用到的几个比较重要的collectionspring
1 //在实例化bean的时候,若是bean正在被建立,会在beforeSingletonCreation()方法中,将当前bean加入到singletonsCurrentlyInCreation 2 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16)); 3 4 5 //从狭义上来理解,spring容器就是signletonObjects这个map;可是,详细点来说,就是由beanDefinition+beanDefinitionMaps+beanFactory+beanFactoryPostprocessor+singletonObjects....等一些列组件的集合 6 //这是spring单实例池(也就是咱们常说的spring容器) 7 private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); 8 9 //咱们姑且称为spring的二级缓存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));在该方法中,会将bean包装为ObjectFactory,存放到map中 10 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); 11 12 //暂时称为三级缓存 三级缓存在循坏依赖中,用来防止重复建立 13 private final Map<String, Object> earlySingletonObjects = new HashMap(16);
上面这几个集合,在源码中会一一解析缓存
上面这里少说了一个变量:allowCircularReferences;这个值默认是true;表示是否容许循环依赖app
2.咱们直接来讲源码吧:学习
org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletonsthis
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanspa
在doGetBean()方法中,咱们首先要说的是Object sharedInstance = getSingleton(beanName);这个方法:debug
1 @Nullable 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 3 Object singletonObject = this.singletonObjects.get(beanName); 4 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 5 synchronized (this.singletonObjects) { 6 //这里只须要从二级缓存中拿一次就行,若是没有二级缓存,每次进来都须要从二级缓存get一次,影响效率 7 singletonObject = this.earlySingletonObjects.get(beanName); 8 if (singletonObject == null && allowEarlyReference) { 9 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 10 if (singletonFactory != null) { 11 singletonObject = singletonFactory.getObject(); 12 this.earlySingletonObjects.put(beanName, singletonObject); 13 this.singletonFactories.remove(beanName); 14 } 15 } 16 } 17 } 18 return singletonObject; 19 }
在第一次初始化bean,走到这个方法的时候,这里返回的确定是null,由于在第一次建立的时候,单实例池、二级缓存、三级缓存中都是null;
这里先这样理解,在后面还会介绍这个方法;在这里返回null以后,spring就会继续往下进行建立初始化的流程;
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
在doGetBean方法中的,有这样一段代码
1 if (mbd.isSingleton()) { 2 sharedInstance = getSingleton(beanName, () -> { 3 try { 4 return createBean(beanName, mbd, args); 5 } 6 catch (BeansException ex) { 7 // Explicitly remove instance from singleton cache: It might have been put there 8 // eagerly by the creation process, to allow for circular reference resolution. 9 // Also remove any beans that received a temporary reference to the bean. 10 destroySingleton(beanName); 11 throw ex; 12 } 13 }); 14 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 15 }
这里判断到当前bean是单实例的时候,在getSingleton方法中有这么一行代码:beforeSingletonCreation(beanName);
这个方法,就是把当前bean添加到了singletonsCurrentlyInCreation这个set集合中;这个set的做用,在后面会用到;
接下来,会继续执行初始化的代码,中间的建立流程不作介绍了哈,直接说关键点
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
在这个方法中,有这么几行重要的代码
1 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 2 isSingletonCurrentlyInCreation(beanName)); 3 if (earlySingletonExposure) { 4 if (logger.isDebugEnabled()) { 5 logger.debug("Eagerly caching bean '" + beanName + 6 "' to allow for resolving potential circular references"); 7 } 8 //第四次调用后置处理器 用来解决循环依赖 9 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 10 }
这段代码的意思是:
若是当前bean是单实例的,而且容许循环依赖;isSingletonCurrentlyInCreation这个方法,就是判断当前bean是否在singletonsCurrentlyInCreation这个set中,也就是说判断当前bean是否正在被建立
这三个条件都知足,因此会调用后置处理器
关键代码是这个this.singletonFactories.put(beanName, singletonFactory); 将后置处理器返回的singletonFactory放到了咱们所说的二级缓存中
再继续执行流程,初始化bean以后,会在populateBean(beanName, mbd, instanceWrapper);这个方法中,进行属性的注入;
关键点就在这里:
咱们就以A类和B类来讲了
2.1 首先要明白的是,上面这一整个流程是第一个A这个bean初始化的过程;A类在进行属性注入的时候,会发现依赖了B类,这时候,会调用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String);
2.2 由于这时候,B类尚未初始化,因此,会进行B类的建立(B类的建立,就是再走一步咱们上面说的流程,),因为流程是如出一辙的,添加到set集合中,调用后置处理器往二级缓存中放入singletonFactory,这些都同样,当B类来到属性注入的时候;会发现B类依赖了A类;也会去调用getBean来判断是否有A类;关键点就是这一次调用getBean的时候
@Nullable 2 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 3 Object singletonObject = this.singletonObjects.get(beanName); 4 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 5 synchronized (this.singletonObjects) { 6 7 singletonObject = this.earlySingletonObjects.get(beanName); 8 if (singletonObject == null && allowEarlyReference) { 9 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 10 if (singletonFactory != null) { 11 singletonObject = singletonFactory.getObject(); 12 this.earlySingletonObjects.put(beanName, singletonObject); 13 this.singletonFactories.remove(beanName); 14 } 15 } 16 } 17 } 18 return singletonObject; 19 }
这一次在调用getBean的时候,singletonObjects里面,仍是获取不到A类,可是:isSingletonCurrentlyInCreation(beanName)这个判断条件会知足,由于A类在初始化的时候,放到了这个set集合中;
这个从earlySingletonObjects中获取不到A类,因此,会执行ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);这一行代码,返回singletonFactory中的object;
2.3 在这一次getBean的时候,A类返回了,因此B类会完成初始化,而后再在A类中注入B类
3 到这里,循环依赖就完成了,不知道有没有描述清楚:
我再描述一下把:
3.1 在A类第一次初始化的时候,会将A类放到表示正在建立的set集合中;将A类放到二级缓存的map中,而后在属性注入的时候,发现A类依赖了B类,会调用getBean,这时候,B类返回的是null,会进行初始化
3.2 B类初始化的流程和A类初始化流程是同样的,将B类放到表示当前bean正在建立的set集合中,将B类放到二级缓存中,而后进行B类的属性注入,在这是,会发现,B类依赖了A类,而后会调用getBean,这时候,虽然spring单实例池中,尚未A类,可是:二级缓存中有,因此就返回了二级缓存中的A类,让B类完成了初始化流程,
3.3 B类完成了初始化,那A类依赖的B也就注入了
最后再贴一张debug的流程图
红色的是A --> B --> A
绿色的是 属性注入具体调用的方法
最后再附上我的在学习spring源码时,在源码上加的注释