Spring容器如何解決循環依賴的原理


Spring容器如何解決循環依賴的原理

最近看源碼在研究類似如下配置的循環依賴是怎么解決的?

1 <bean id="a" class="com.project.demo.A" scope="singleton">
2       <property name="b" ref="b"/>
3 </bean>
4 <bean id="b" class="com.project.demo.B" scope="singleton">
5       <property name="a" ref="a"/>
6 </bean>

 

說明:

  1、Spring容器解決循環依賴的問題配置類必須是單例模式scope="singleton"才支持,如果是scope="prototype"是無法解決循環依賴的。

  2、Spring容器解決循環依賴主要依靠三級緩存機制

    2.1 一級緩存使用的map: private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

    2.2 二級緩存使用的map: private final Map<String, Object> earlySingletonObjects = new HashMap(16); 

    2.3 三級緩存使用的map: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

  3、Spring容器解決循環依賴簡潔概述主要有四大流程方法:獲取對象 getSingleton()、 創建對象(實例化) doCreateBean()、填充屬性(初始化) populateBean()、返回對象 addSingleton()

 

在系統啟動獲取配置文件后,程序是依次讀取並加載的,所以當執行上面配置文件時,先獲取a對象,如果獲取不到則先實例化a對象,然后初始化a對象即給a添加b屬性,在添加b屬性的時候先獲取b,如果b存在則直接給a設置,不存在則實例化b,並且初始化b對象即給b添加對應的屬性。

那么在代碼執行過程中,先調用getSingleton()方法,我們查看源碼

 1     @Nullable
 2     public Object getSingleton(String beanName) {
// 調用下方重載方法
3 return this.getSingleton(beanName, true); 4 } 5 6 @Nullable 7 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       // 先從一級緩存中獲取a對象的實例
8 Object singletonObject = this.singletonObjects.get(beanName);
// 如果從一級緩存中獲取不到a對象,那么檢查該對象是否正在被創建,如果正在被創建,則進入if循環中
9 if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { 10 synchronized(this.singletonObjects) {
            // 從二級緩存中獲取該對象
11 singletonObject = this.earlySingletonObjects.get(beanName);
// 如果二級緩存中無法獲取該對象,那么一定會進入如下if方法,因為allowEarlyReference傳過來的時候就是true
12 if (singletonObject == null && allowEarlyReference) {
              // 從三級緩存中獲取該對象
13 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); 14 if (singletonFactory != null) {
// 如果獲取到了該對象,就將三級緩存中的對象放到二級緩存中,並且將三級緩存中的對象刪除
15 singletonObject = singletonFactory.getObject(); 16 this.earlySingletonObjects.put(beanName, singletonObject); 17 this.singletonFactories.remove(beanName); 18 } 19 } 20 } 21 } 22 23 return singletonObject; 24 }

從三面的源碼發現,如果a第一次獲取,那么第9行的if語句為false,將直接放回為null,這時回到創建對象doCreateBean()方法,該方法使用反射的方式生成a對象,並且該對象在三級緩存中,對象生成后就需要對a對象進行屬性填充:

 1 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 2         // 省略多行代碼,大致就是調用各種方法,通過反射創建對象
 3         try {
 4             // a對象創建完成,調用屬性填充方法,對a進行屬性填充
 5             this.populateBean(beanName, mbd, instanceWrapper);
 6             exposedObject = this.initializeBean(beanName, exposedObject, mbd);
 7         } catch (Throwable var18) {
 8             if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
 9                 throw (BeanCreationException)var18;
10             }
11 
12             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
13         }
14 
15         if (earlySingletonExposure) {
16             Object earlySingletonReference = this.getSingleton(beanName, false);
17             if (earlySingletonReference != null) {
18                 // 省略多行代碼
19             }
20         }
22         // 省略多行代碼
23     }

在上面代碼doCreateBean()方法中先創建a對象,創建完成后會調用this.populateBean(beanName, mbd, instanceWrapper)方法對a進行屬性填出,這個時候會獲取配置文件中所有<bean id="a">里面的所有屬性,發現會存在一個b屬性,下面貼出部分源碼

 1 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
 2         if (bw == null) {
 3             if (mbd.hasPropertyValues()) {
 4                 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
 5             }
 6         } else {
 7             boolean continueWithPropertyPopulation = true;
 8             if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
 9                 // 刪除大量代碼
10             }
11 
12             if (continueWithPropertyPopulation) {
13                 // 刪除大量源代碼,applyPropertyValues方法中beanName為a,pvs為狀態各種屬性的PropertyValues對象,pvs就裝有b這個屬性
14                 if (pvs != null) {
15                     this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
16                 }
17 
18             }
19         }
20     }

繼續跟進applyPropertyValues方法的源碼

 1 protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
 2         if (!pvs.isEmpty()) {
 3             if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
 4                 ((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());
 5             }
 6             MutablePropertyValues mpvs = null;
 7             List original;
 8             if (pvs instanceof MutablePropertyValues) {
 9                 // 省略大量代碼
10             } else {
11                 original = Arrays.asList(pvs.getPropertyValues());
12             }
13             // 省略大量代碼 大致過程是將屬性對象pvs 轉化成original List對象,然后在使用迭代器在下面進行迭代
14             Iterator var11 = original.iterator();
15             while(true) {
16                 while(var11.hasNext()) {
17                     PropertyValue pv = (PropertyValue)var11.next();
18                     if (pv.isConverted()) {
19                         deepCopy.add(pv);
20                     } else {
21                         String propertyName = pv.getName();
22                         Object originalValue = pv.getValue();
23                         // 通過下面方法解決依賴的b,整個方法在迭代器中,外層在while(true)中,可能有多個屬性,循環直到所有屬性都解決了就return;或者拋出異常
24                         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
25                         // 省略大量代碼
26                     }
27                 }
28 
29                 if (mpvs != null && !resolveNecessary) {
30                     mpvs.setConverted();
31                 }
32 
33                 try {
34                     bw.setPropertyValues(new MutablePropertyValues(deepCopy));
35                     return;
36                 } catch (BeansException var19) {
37                     throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var19);
38                 }
39             }
40         }
41     }

繼續跟進上面紅色方法

1 public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
2         if (value instanceof RuntimeBeanReference) {
3             RuntimeBeanReference ref = (RuntimeBeanReference)value;
4             return this.resolveReference(argName, ref);
5         } else if (value instanceof RuntimeBeanNameReference) {
6             // 省略多行代碼
7         } 
8         // 省略多行代碼
9     }

繼續跟進紅色部分的代碼

 1 private Object resolveReference(Object argName, RuntimeBeanReference ref) {
 2         try {
 3             String refName = ref.getBeanName();
 4             refName = String.valueOf(this.doEvaluate(refName));
 5             Object bean;
 6             if (ref.isToParent()) {
 7                 // 省略多行代碼
 8             } else {
 9                 // 通過refName的值b又去工廠找b對象
10                 bean = this.beanFactory.getBean(refName);
11                 this.beanFactory.registerDependentBean(refName, this.beanName);
12             }
13 
14             // 省略多行代碼
15             return bean;
16         } catch (BeansException var5) {
17             throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, var5);
18         }
19     }
1     public Object getBean(String name) throws BeansException {
2         return this.doGetBean(name, (Class)null, (Object[])null, false);
3     }
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        // 跟了這么就,最終表明在實例化a對象后,在裝載a的屬性b時,會經過各種校驗最終到getSingleton(),及先獲取b對象,如果b對象不存在則會對b完成創建的過程
        Object sharedInstance = this.getSingleton(beanName);
        // 省略多行代碼
    }

上面整個過程概括:在檢測到需要實例化a時,先去獲取a對象,看a是否已經存在,獲取去先從一級緩存中獲取,如果沒有並且如果a也沒有正在實例化,那么直接返回null,表明獲取不到a對象,那么此時調用doCreateBean()方法完成對a對象的實例化過程(通過反射創建a對象),並且將創建的a對象放在三級緩存中,然后繼續執行doCreateBean中的populateBean()方法完成對a進行初始化即添加屬性b,經過一系列校驗,最終又會調用getSingleton()方法來獲取b對象,同樣會返回null,這個時候就會去執行doCreateBean()方法創建b對象,同樣過反射創建b,當b對象創建完成時也會存放在三級緩存中,在實例化b對象完成,然后繼續執行doCreateBean中的populateBean()方法,也需要初始化b對象,填充b的屬性,這時發現b對象的屬性是a,同樣再次通過getSingleton()方法獲取a,獲取a的過程如第一個源碼部分,先從一級緩存中獲取,獲取不到,然后判斷a正在創建中,然后就從二級、三級緩存中獲取,最終在三級緩存中獲取到了a,並且將三級緩存中的a對象放到二級緩存中,並將刪除三級緩存中的a,此時b對象初始化也完成。

在a對象初始化的流程中,將b對象也實例化和初始化了,在b的初始化過程中,將a從三級緩存移到了二級緩存中,當b初始化完成后繼續向下執行,會執行到addSingleton(),查看源碼

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {
       // 將b加入到1級緩存
this.singletonObjects.put(beanName, singletonObject);
// 將b從三級緩存中刪除
this.singletonFactories.remove(beanName);
// 將b從二級緩存中刪除(b在二級緩存中沒有,即空刪除)
this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }

從上面代碼可以看出在,並創建完成並且初始化后,會將B從三級緩存中直接放到一級緩存中,並且刪除三級緩存中的數據。

所有b工作做完后返回到a初始化屬性b的代碼

 1     protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 2 
 3         // 省略大量代碼
 4         try {
 5             // 完成a對於屬性b的檢查裝配工作后返回到方法populateBean(),繼續向下執行
 6             this.populateBean(beanName, mbd, instanceWrapper);
 7             exposedObject = this.initializeBean(beanName, exposedObject, mbd);
 8         } catch (Throwable var18) {
 9             if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
10                 throw (BeanCreationException)var18;
11             }
12 
13             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
14         }
15 
16         if (earlySingletonExposure) {
17             // a執行到該方法getSingleton(),這里面會將a從二級緩存中取出來
18             Object earlySingletonReference = this.getSingleton(beanName, false);
19             if (earlySingletonReference != null) {
20                 // 省略大量代碼
21             }
22         }
23         // 省略大量代碼
24     }

繼續執行,最終也會到addSingleton()方法,將a也加入一級緩存,並且從二級緩存中刪除a。

這樣a和b兩個循環依賴的bean都被放入到一級緩存中。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM