曹工說Spring Boot源碼(29)– Spring 解決循環依賴為什麼使用三級緩存,而不是二級緩存_網頁設計公司_網頁設計公司

曹工說Spring Boot源碼(29)– Spring 解決循環依賴為什麼使用三級緩存,而不是二級緩存_網頁設計公司_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

寫在前面的話

相關背景及資源:

曹工說Spring Boot源碼(1)– Bean Definition到底是什麼,附spring思維導圖分享

曹工說Spring Boot源碼(2)– Bean Definition到底是什麼,咱們對著接口,逐個方法講解

曹工說Spring Boot源碼(3)– 手動註冊Bean Definition不比遊戲好玩嗎,我們來試一下

曹工說Spring Boot源碼(4)– 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?

曹工說Spring Boot源碼(5)– 怎麼從properties文件讀取bean

曹工說Spring Boot源碼(6)– Spring怎麼從xml文件裡解析bean的

曹工說Spring Boot源碼(7)– Spring解析xml文件,到底從中得到了什麼(上)

曹工說Spring Boot源碼(8)– Spring解析xml文件,到底從中得到了什麼(util命名空間)

曹工說Spring Boot源碼(9)– Spring解析xml文件,到底從中得到了什麼(context命名空間上)

曹工說Spring Boot源碼(10)– Spring解析xml文件,到底從中得到了什麼(context:annotation-config 解析)

曹工說Spring Boot源碼(11)– context:component-scan,你真的會用嗎(這次來說說它的奇技淫巧)

曹工說Spring Boot源碼(12)– Spring解析xml文件,到底從中得到了什麼(context:component-scan完整解析)

曹工說Spring Boot源碼(13)– AspectJ的運行時織入(Load-Time-Weaving),基本內容是講清楚了(附源碼)

曹工說Spring Boot源碼(14)– AspectJ的Load-Time-Weaving的兩種實現方式細細講解,以及怎麼和Spring Instrumentation集成

曹工說Spring Boot源碼(15)– Spring從xml文件裡到底得到了什麼(context:load-time-weaver 完整解析)

曹工說Spring Boot源碼(16)– Spring從xml文件裡到底得到了什麼(aop:config完整解析【上】)

曹工說Spring Boot源碼(17)– Spring從xml文件裡到底得到了什麼(aop:config完整解析【中】)

曹工說Spring Boot源碼(18)– Spring AOP源碼分析三部曲,終於快講完了(aop:config完整解析【下】)

曹工說Spring Boot源碼(19)– Spring 帶給我們的工具利器,創建代理不用愁(ProxyFactory)

曹工說Spring Boot源碼(20)– 碼網恢恢,疏而不漏,如何記錄Spring RedisTemplate每次操作日誌

曹工說Spring Boot源碼(21)– 為了讓大家理解Spring Aop利器ProxyFactory,我已經拼了

曹工說Spring Boot源碼(22)– 你說我Spring Aop依賴AspectJ,我依賴它什麼了

曹工說Spring Boot源碼(23)– ASM又立功了,Spring原來是這麼遞歸獲取註解的元註解的

曹工說Spring Boot源碼(24)– Spring註解掃描的瑞士軍刀,asm技術實戰(上)

曹工說Spring Boot源碼(25)– Spring註解掃描的瑞士軍刀,ASM + Java Instrumentation,順便提提Jar包破解

曹工說Spring Boot源碼(26)– 學習字節碼也太難了,實在不能忍受了,寫了個小小的字節碼執行引擎

曹工說Spring Boot源碼(27)– Spring的component-scan,光是include-filter屬性的各種配置方式,就夠玩半天了

曹工說Spring Boot源碼(28)– Spring的component-scan機制,讓你自己來進行簡單實現,怎麼辦

工程代碼地址思維導圖地址

工程結構圖:

什麼是三級緩存

在獲取單例bean的時候,會進入以下方法:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
    
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
                // 1
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
                                // 2
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
                                        // 3
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                                                // 4
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

這裏面涉及到了該類中的三個field。

	/** 1級緩存 Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** 2級緩存 Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	/** 3級緩存 Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

接著說前面的代碼。

  • 1處,在最上層的緩存singletonObjects中,獲取單例bean,這裏面拿到的bean,直接可以使用;如果沒取到,則進入2處

  • 2處,在2級緩存earlySingletonObjects中,查找bean;

  • 3處,如果在2級緩存中,還是沒找到,則在3級緩存中查找對應的工廠對象,利用拿到的工廠對象(工廠對像中,有3個field,一個是beanName,一個是RootBeanDefinition ,一個是已經創建好的,但還沒有註入屬性的bean),去獲取包裝後的bean,或者說,代理後的bean。

    什麼是已經創建好的,但沒有註入屬性的bean?

    比如一個bean,有10個字段,你new了之後,對像已經有了,內存空間已經開闢了,堆裡已經分配了該對象的空間了,只是此時的10個field還是null。

ioc容器,普通循環依賴,一級緩存夠用嗎

說實話,如果簡單寫寫的話,一級緩存都沒問題。給大家看一個我以前寫的渣渣ioc容器:

曹工說Tomcat4:利用Digester 手擼一個輕量的Spring IOC容器

@Data
public class BeanDefinitionRegistry {
    /**
     * map:存儲 bean的class-》bean實例
     */
    private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>();
    
    /**
     * 根據bean 定義獲取bean
     * 1、先查bean容器,查到則返回
     * 2、生成bean,放進容器(此時,依賴還沒注入,主要是解決循環依賴問題)
     * 3、注入依賴
     *
     * @param beanDefiniton
     * @return
     */
    private Object getBean(MyBeanDefiniton beanDefiniton) {
        Class<?> beanClazz = beanDefiniton.getBeanClazz();
        Object bean = beanMapByClass.get(beanClazz);
        if (bean != null) {
            return bean;
        }
		// 0
        bean = generateBeanInstance(beanClazz);


        // 1 先行暴露,解決循環依賴問題
        beanMapByClass.put(beanClazz, bean);
        beanMapByName.put(beanDefiniton.getBeanName(), bean);

        // 2 查找依賴
        List<Field> dependencysByField = beanDefiniton.getDependencysByField();
        if (dependencysByField == null) {
            return bean;
        }
		
        // 3
        for (Field field : dependencysByField) {
            try {
                autowireField(beanClazz, bean, field);
            } catch (Exception e) {
                throw new RuntimeException(beanClazz.getName() + " 創建失敗",e);
            }
        }

        return bean;
    }
}

大家看上面的代碼,我只定義了一個field,就是一個map,存放bean的class-》bean。

    /**
     * map:存儲 bean的class-》bean實例
     */
    private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>();
  • 0處,生成bean,直接就是new
  • 1處,先把這個不完整的bean,放進map
  • 2處,獲取需要注入的屬性集合
  • 3處,進行自動注入,就是根據field的Class,去map裡查找對應的bean,設置到field裡。

上面這個代碼,有啥問題沒?spring為啥整整三級?

ioc,一級緩存有什麼問題

一級緩存的問題在於,就1個map,裏面既有完整的已經ready的bean,也有不完整的,尚未設置field的bean。

如果這時候,有其他線程去這個map裡獲取bean來用怎麼辦?拿到的bean,不完整,怎麼辦呢?屬性都是null,直接空指針了。

所以,我們就要加一個map,這個map,用來存放那種不完整的bean。這裏,還是拿spring舉例。我們可以只用下面這兩層:

	/** 1級緩存 Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** 2級緩存 Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

因為spring代碼裡是三級緩存,所以我們對源碼做一點修改。

修改spring源碼,只使用二級緩存

修改創建bean的代碼,不放入第三級緩存,只放入第二級緩存

創建了bean之後,屬性注入之前,將創建出來的不完整bean,放到earlySingletonObjects

這個代碼,在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean,我這邊只有4.0版本的spring源碼工程,不過這套邏輯,算是spring核心邏輯,和5.x版本差別不大。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            // 1
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
		Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
		...
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
            // 2
			earlySingletonObjects.put(beanName,bean);
			registeredSingletonObjects.add(beanName);
			// 3
//			addSingletonFactory(beanName, new ObjectFactory() {
//				public Object getObject() throws BeansException {
//					return getEarlyBeanReference(beanName, mbd, bean);
//				}
//			});
		}
  • 1處,就是創建對象,就是new
  • 2處,這是我加的代碼,放入二級緩存
  • 3處,本來這就是增加三級緩存的位置,被我註釋了。現在,就不會往三級緩存放東西了

修改獲取bean的代碼,只從第一、第二級緩存獲取,不從第三級獲取

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

之前的代碼是文章開頭那樣的,我這裏修改為:

	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);
				return singletonObject;
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);

這樣,就是只用兩級緩存了。

兩級緩存,有啥問題?

ioc循環依賴,一點問題都沒有,完全夠用了。

我這邊一個簡單的例子,


public class Chick{
    private Egg egg;

    public Egg getEgg() {
        return egg;
    }

    public void setEgg(Egg egg) {
        this.egg = egg;
    }
}

public class Egg {
    private Chick chick;

    public Chick getChick() {
        return chick;
    }

    public void setChick(Chick chick) {
        this.chick = chick;
    }
    <bean id="chick" class="foo.Chick" lazy-init="true">
        <property name="egg" ref="egg"/>
    </bean>
    <bean id="egg" class="foo.Egg" lazy-init="true">
        <property name="chick" ref="chick"/>
    </bean>
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
                "context-namespace-test-aop.xml");

        Egg egg = (Egg) ctx.getBean(Egg.class);

結論:

所以,一級緩存都能解決的問題,二級當然更沒問題。

但是,如果我這裏給上面的Egg類,加個切面(aop的邏輯,意思就是最終會生成Egg的一個動態代理對象),那還有問題沒?

    <aop:config>
        <aop:pointcut id="mypointcut" expression="execution(public * foo.Egg.*(..))"/>
        <aop:aspect id="myAspect" ref="performenceAspect">
            <aop:after method="afterIncubate" pointcut-ref="mypointcut"/>
        </aop:aspect>
    </aop:config>

注意這裏的切點:

execution(public * foo.Egg.*(..))

就是切Egg類的方法。

加了這個邏輯後,我們繼續運行,在 Egg egg = (Egg) ctx.getBean(Egg.class);行,會拋出如下異常:

我塗掉了一部分,因為那是官方對這個異常的推論,因為我們改了代碼,所以推論不準確,因此乾脆隱去。

這個異常是說:

兄弟啊,bean egg已經被注入到了其他bean:chick中。(因為我們循環依賴了),但是,注入到chick中的,是Egg類型。但是,我們這裏最後對egg這個bean,進行了後置處理,生成了代理對象。那其他bean裡,用原始的bean,是不是不太對啊?

所以,spring給我們拋錯了。

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

怎麼理解呢?以io流舉例,我們一開始都是用的原始字節流,然後給別人用的也是字節流,但是,最後,我感覺不方便,我自己悄悄弄了個緩存字符流(類比代理對象) ,我是方便了,但是,別人用的,還是原始的字節流啊。

你bean不是單例嗎?不能這麼玩吧?

所以,這就是二級緩存,不能解決的問題。

什麼問題?aop情形下,注入到其他bean的,不是最終的代理對象。

三級緩存,怎麼解決這個問題

要解決這個問題,必須在其他bean(chick),來查找我們(以上面例子為例,我們是egg)的時候,查找到最終形態的egg,即代理後的egg。

怎麼做到這點呢?

加個三級緩存,裏面不存具體的bean,裏面存一個工廠對象。通過工廠對象,是可以拿到最終形態的代理後的egg。

ok,我們將前面修改的代碼還原:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            // 1
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
		Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
            // 2
//			Map<String, Object> earlySingletonObjects = this.getEarlySingletonObjects();
//			earlySingletonObjects.put(beanName,bean);
//
//			Set<String> registeredSingletonObjects = this.getRegisteredSingletonObjects();
//			registeredSingletonObjects.add(beanName);
			
            // 3
			addSingletonFactory(beanName, new ObjectFactory() {
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}
  • 1處,創建bean,單純new,不注入

  • 2處,revert我們的代碼

  • 3處,這裏new了一個ObjectFactory,然後會存入到如下的第三級緩存。

    	/** 3級緩存 Cache of singleton factories: bean name to ObjectFactory. */
    	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    

    注意,new一個匿名內部類(假設這個匿名類叫AA)的對象,其中用到的外部類的變量,都會在AA中隱式生成對應的field。

    大家看上圖,裏面的3個字段,和下面代碼1處中的,幾個字段,是一一對應的。

    			addSingletonFactory(beanName, new ObjectFactory() {
    				public Object getObject() throws BeansException {
                        // 1
    					return getEarlyBeanReference(beanName, mbd, bean);
    				}
    			});
    

ok,現在,egg已經把自己存進去了,存在了第三級緩存,1級和2級都沒有,那後續chick在使用getSingleton查找egg的時候,就會進入下面的邏輯了(就是文章開頭的那段代碼,下面已經把我們的修改還原了):

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);
//				return singletonObject;
//			}
//		}
//		return (singletonObject != NULL_OBJECT ? singletonObject : null);

		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) {
                        // 1
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

上面就會進入1處,調用singletonFactory.getObject();

而前面我們知道,這個factory的邏輯是:

			addSingletonFactory(beanName, new ObjectFactory() {
				public Object getObject() throws BeansException {
                    // 1
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});

1處就是這個工廠方法的邏輯,這裏面,簡單說,就會去調用各個beanPostProcessor的getEarlyBeanReference方法。

其中,主要就是aop的主力beanPostProcessor,AbstractAutoProxyCreator#getEarlyBeanReference

其實現如下:

	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.add(cacheKey);
        // 1
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

這裏的1處,就會去對egg這個bean,創建代理,此時,返回的對象,就是個代理對象了,那,注入到chick的,自然也是代理後的egg了。

關於SmartInstantiationAwareBeanPostProcessor

我們上面說的那個getEarlyBeanReference就在這個接口中。

這個接口繼承了BeanPostProcessor

而創建代理對象,目前就是在如下兩個方法中去創建:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;    
}

這兩個方法,都是在實例化之後,創建代理。那我們前面創建代理,是在依賴解析過程中:

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
    ...
	Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
}

所以,spring希望我們,在這幾處,要返回同樣的對象,即:既然你這幾處都要返回代理對象,那就不能返回不一樣的代理對象。

那我們再看看,到底,AbstractAutoProxyCreator有沒有遵守約定呢,這幾個方法裡,有沒有去返回同樣的代理包裝對象呢?

getEarlyBeanReference

	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 1
		this.earlyProxyReferences.add(cacheKey);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

1處,往field:

private final Set<Object> earlyProxyReferences =
      Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>(16));

裡,加了個cachekey,這個cachekey,主要也就是如下的字符串,用來唯一標識而已。

	protected Object getCacheKey(Class<?> beanClass, String beanName) {
		return beanClass.getName() + "_" + beanName;
	}

我們可以看看這個field在哪裡被用到了。

也就兩處,一處就是當前位置;另外一處,下面講。

這裏,主要就是看看到底要不要生成代理對象,要的話,就生成,不要就算了,另外,做了個標記:在earlyProxyReferences加了當前bean的key,表示:當前bean,已經被getEarlyBeanReference方法處理過了。

至於,最終到底有沒有生成代理對象,另說。畢竟調用wrapIfNecessary也不是說,一定就滿足切面,要生成代理對象。

可能返回的仍然是原始對象。

postProcessBeforeInitialization

public Object postProcessBeforeInitialization(Object bean, String beanName) {
   return bean;
}

這一處,沒做處理。

postProcessAfterInitialization

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 1
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

這裏,1處這個判斷哈,就用到了前面我們說的那個field。那個field,只在兩處用,一處就是調用getEarlyBeanReference,會往裡面把當前bean的key放進去;另外一處,就是這裏。

這裏判斷,如果field裡不包含當前bean,就去調用wrapIfNecessary;如果包含(意味著,getEarlyBeanReference處理過了),就不調用了。

這裏,說到底,就是保證了,wrapIfNecessary只被調用一次。

看吧,wrapIfNecessary也就這兩處被調用了。

所以,我們可以得出結論,在aop這個beanPostProcessor中,有多處機會可以返回一個proxy對象,但是,最終,只要在其中一處處理了,其他處,根本不再繼續處理。

另外,還有一點很重要,在這個aop beanPostProcessor中,傳入了原始的bean,我們會去判斷,是否要給它創建代理,如果要,就創建;如果不要則:

返回原始對象

整個流程串起來

上面這個後置處理器看明白了,接下來,再看看創建bean的核心流程:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		// 1 
		BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
		final Object bean = instanceWrapper.getWrappedInstance();
		
		if (earlySingletonExposure) {
            // 2
			addSingletonFactory(beanName, new ObjectFactory() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// 3 
		Object exposedObject = bean;
    	// 4
        populateBean(beanName, mbd, instanceWrapper);
		
    	// 5
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }

		if (earlySingletonExposure) {
            // 6
			Object earlySingletonReference = getSingleton(beanName, false);
            
			if (earlySingletonReference != null) {
                // 7
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    // 8
                    ...
				}
			}
		}

		return exposedObject;
	}

上面流程中,做了部分刪減。但基本創建一個bean,就這幾步了。

  • 1處,創建bean對象,此時,屬性什麼的全是null,可以理解為,只是new了,field還沒設置

  • 2處,添加到第三級緩存;加進去的,只是個factory,只有循環依賴的時候,才會發揮作用

  • 3處,把原始bean,存到exposedObject

  • 4處,填充屬性;循環依賴情況下,A/B循環依賴。假設當前為A,那麼此時填充A的屬性的時候,會去:

    new B;

    填充B的field,發現field裡有一個是A類型,然後就去getBean(“A”),然後走到第三級緩存,拿到了A的ObjectFactory,然後調用ObjectFactory,然後調用AOP的後置處理器類:getEarlyBeanReference,拿到代理後的bean(假設此處切面滿足,要創建代理);

    經過上面的步驟後,B裏面,field已經填充ok,其中,且填充的field是代理後的A,這裏命名為proxy A。

    B 繼續其他的後續處理。

    B處理完成後,被填充到當前的origin A(原始A)的field中

  • 5處,對A進行後置處理,此時調用aop後置處理器的,postProcessAfterInitialization;前面我們說了,此時不會再去調用wrapIfNecessary,所以這裏直接返回原始A,即origin A

  • 6處,去緩存裡獲取A,拿到的A,是proxy A

  • 7處,我們梳理下:

    exposedObject:origin A

    bean:原始A

    earlySingletonReference: proxy A

    此時,下面這個條件是滿足的,所以,exposedObject,最終被替換為proxy A:

    if (exposedObject == bean) {
        exposedObject = earlySingletonReference;
    }
    

源碼

文章用到的aop循環依賴的demo,自己寫一個也可以,很簡單:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-aop-xml-demo-cycle-reference

不錯的參考資料

https://blog.csdn.net/f641385712/article/details/92801300

總結

如果有問題,歡迎指出;歡迎加群討論;有幫助的話,請點個贊吧,謝謝

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌