循環依賴是指兩個或者多個Bean以前相互持有對方。在Spring中循環依賴通常有三種方式:java
在Spring中構造函數循環依賴是沒法解決的,由於構造函數依賴實際上是方法間循環調用的一種,會發生死循環。可是在Spring中會直接拋出BeanCurrentlyInCreationException
異常。源碼以下:git
// 在緩存中獲取Bean,若是沒有就建立Bean public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // 在緩存中獲取Bean Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 判斷容器是否正在銷燬單實例Bean if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } // 將當前須要建立的Bean標示放到Set集合,若是失敗則拋出BeanCurrentlyInCreationException異常 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<Exception>(); } try { // 建立Bean實例 singletonObject = singletonFactory.getObject(); newSingleton = true; } ... if (newSingleton) { // 將Bean實例註冊到singletonObjects容器中 addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } } protected void beforeSingletonCreation(String beanName) { // 將當前須要建立的Bean標示方法Set集合,若是失敗則拋出BeanCurrentlyInCreationException異常 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
執行過程:github
BeanCurrentlyInCreationException
異常解決構造函數依賴主要是第3步實現的,Spring在容器建立的Bean的時候,會將Bean的標示(name)放到一個Set集合裏面(當前正在建立的Bean池)。當在建立Bean的過程當中,發現自已經在這個Set集合中時,就直接會拋出
BeanCurrentlyInCreationException
異常,而不會發生死循環。spring
@Service public class AService { @Autowired private BService bService; @Autowired public void setbService(BService bService) { this.bService = bService; } }
這兩種方式都算是setter方法依賴。咱們建立單實例Bean的大體過程能夠劃分紅三個階段:緩存
createBeanInstance(beanName, mbd, args);
populateBean(beanName, mbd, instanceWrapper);
initializeBean(beanName, exposedObject, mbd);
對於Setter注入形成的循環依賴,Spring容器是在建立Bean第一步實例化後,就將Bean的引用提早暴露出來。經過提早暴露出一個單例工廠方法,從而使得其餘Bean能夠引用到該Bean。app
建立Bean時提早暴露剛完成第一步的Bean,源碼以下:ide
addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); // 將單例工廠放入緩存中 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
自動裝配過程當中獲取單實例Bean,源碼以下:函數
protected Object getSingleton(String beanName, boolean allowEarlyReference) { 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 != NULL_OBJECT ? singletonObject : null); }
咱們能夠看到,在自動裝配Bean的過程當中,會去找三個緩存:spring-boot
這裏爲何會用一個三級緩存呢,爲啥不直接將只完成了建立Bean的第一步的Bean直接方到
earlySingletonObjects
緩存中呢?this咱們跟入
getEarlyBeanReference(beanName, mbd, bean)
咱們能夠發現,單實例工廠方法返回Bean的時候還執行了後置處理器的,在後置處理器中咱們還能夠對Bean進行一些特殊處理。若是咱們直接將剛完成實例化的Bean放入earlySingletonObjects
緩存中,那麼失去對Bean進行特殊處理的機會。
源碼以下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return null; } } } } return exposedObject; }
對於
singleton
做用域 bean ,能夠經過setAllowCircularReferences(false);
來禁用循環引用。
對於prototype
做用域 bean, Spring 容器沒法完成依賴注入,由於 Spring 容器不進行緩存prototype
做用域的 bean ,所以沒法提早暴露一個建立中的 bean 示。
https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases
spring-boot-student-spring 工程
《Spring源碼深度解析》