深入理解 Spring 循環依賴之三級緩存(附源碼分析)

前言:

學過 Spring 的都知道 Spring 利用三級緩存解決了循環依賴問題,那你知道什么是循環依賴?什么又是三級緩存?本篇將從源碼層面分析 Spring 是怎么去利用三級緩存幫我們解決循環依賴問題。

深入理解 Spring IOC 底層實現機制(refresh 方法源碼分析)

Spring 源碼之 BeanDefinition 加載分析

深入理解 Spring Bean 生命周期(附源碼分析)

什么是循環依賴?

簡單來說 如果 Bean A 依賴了 Bean B,而 Bean B 又依賴了 Bean A,造成了相互依賴,也就是我們常說的的循環依賴,代碼演示如下:

@Service
public class A{@Resourceprivate B b;
}@Service
public class B{@Resourceprivate A a ;
}

什么是三級緩存?

三級緩存是 Spring 為了解決循環依賴問題而設計的,在 Spring 源碼中是三個 Map 存儲,如下:

// 一級緩存 存放完整的Bean(實例化 初始化完成的 bean)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);// 二級緩存Map 存放不完整的Bean(只實例化完 還沒屬性賦值、初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);// 三級緩存Map 存放一個Bean的lambda表達式(也是我們常說的早期bean)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
  • 一級緩存(singletonObjects):用來存儲成品 Bean 對象,也就是初始化完成可以直接被其他對象引用的對象。
  • 二級緩存(earlySingletonObjects):用來存儲半成品對象,也就是實例化了但是還沒有初始化的對象。
  • 三級緩存(singletonFactories):用來存儲工廠對象,二級緩存就是從這個工廠中獲取的對象,也就是一個 lambda 表達式,這個 lambda 表達式會提前暴露 Bean 對象的引用,執行這個 lambda 表達式會把這個引用放入二級緩存,同時刪除三級緩存。

圖解 Spring 三級緩存解決循環依賴:

我們用畫圖的方法來理解一下 Bean A 依賴 Bean B,Bean B又依賴 Bean A 的情況下,Spring 是怎么去進行 Bean A 和 Bean B 的加載的。

在這里插入圖片描述

Spring 解決循環依賴的核心就是暴露早期對象,將對象的實例化和初始化分開。

循環依賴場景面試問題

源碼分析部分較長,我們先把關于 Spring 三級緩存解決循環依賴的常見問題科普一下。

三個緩存對象的查找順序是什么樣子的?

這個問題閱讀了源碼的都知道是先找一級緩存,找不到再找二級緩存,最后才會找三級緩存。

一級緩存能否解決循環依賴問題?

不能,一級緩存和二級緩存存儲的是不同類型的對象,如果只有一級緩存的話,那么成品對象和半成品對象會存放在一起,半成品狀態的對象是直接接暴露給其他對象做引用的,放在一起就無法判斷哪些是半成品對象那些事成品對象了。

二級緩存能否解決循環依賴問題?

可以解決某些情況下的循環依賴問題,但是有限制條件,不能出現代理對対象,例如 AOP 的場景、事務的場景。

為什么必須要使用三級緩存?

三級緩存的設計目的主要是針對 AOP 等需要代理場景的 Bean 對象,三級緩存是一個單例工廠,這個工廠的目的是延遲實例化階段生成對象的代理,只有當 Bean 對象真正發生循環依賴時候,才會提前去生成代理對象,否則只會創建一個工廠放入三級緩存中,但是不會通過這個工廠去創建真正的對象,如果沒有三級緩存的話,意味著所有 Bean 對象都需要在提前去生成代理對象,這違背了 Spring 的設計,Spring 設計之初是讓 Bean 在生命周期的最后一步完成代理,而不是在實例化后就立馬完成代理,Spring 結合 AOP 是通過 AnnotationAwareAspectJAutoProxyCreator 這個后置處理器來完成的,在這個后置處理的 postProcessAfterInitialization 方法中對初始化后的 Bean 完成 AOP 代理,如果出現了循環依賴,沒有辦法,只有給 Bean 先創建代理。

三級緩存為什么不能解決構造器引起的循環依賴?

因為構造器引起的循環依賴是發生在實例化階段,而 Spring 使用一、二、三級緩存解決循環依賴的思想是把實例化和初始化分開,暴露早期實例化但是沒有初始化的對象,構造器引起的循環依賴是發生在實例化階段,自然就無法使用三級緩存來解決了。

Spring 三級緩存解決循環依賴源碼分析

Spring 容器中的 Bean 獲取是如下流程:

getBean–>doGetBean–>createBean–>doCreateBean
分析 Spring 使用三級緩存解決循環依賴,實際就是分析獲取 Bean 的過程,當然這個過程包含創建 Bean,我們從 doGetBean 方法開始分析。

AbstractBeanFactory#doGetBean 方法源碼分析

AbstractBeanFactory#doGetBean 方法在 Spring Bean 生命周期中分析過,本篇重點分析和三級緩存相關的點,比如 this.getSingleton(beanName) 方法。

//AbstractBeanFactory#doGetBean 方法
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {//根據 name 獲取beanName 如果是別名會將別名轉換為規范的名稱String beanName = this.transformedBeanName(name);//根據beanName 從容器中獲取 bean 對象(包含一級緩存 二級緩存 三級緩存)  本篇分析的重點Object sharedInstance = this.getSingleton(beanName);Object bean;//bean 不為空 直接從IOC 容器中獲取 beanif (sharedInstance != null && args == null) {if (this.logger.isTraceEnabled()) {if (this.isSingletonCurrentlyInCreation(beanName)) {this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");} else {this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}//對 bean 進行 BeanFactory 的相關操作(不是本次分析的重點)bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);} else {//表示IOC 容器中沒有當前單例 beanif (this.isPrototypeCurrentlyInCreation(beanName)) {//IOC 容器中正在創建原型bean 原型bean 中出現循環依賴直接拋出 bean 創建異常throw new BeanCurrentlyInCreationException(beanName);}//獲取父級 BeanFactory  IOC 容器BeanFactory parentBeanFactory = this.getParentBeanFactory();if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {//當前IOC容器的父容器存在 且當前容器中不存在當前beanName的 beanDefinition//解析指定 Bean 名稱的原始名稱String nameToLookup = this.originalBeanName(name);//父IOC 容器是 AbstractBeanFactory if (parentBeanFactory instanceof AbstractBeanFactory) {//繼續調用AbstractBeanFactory#doGetBean 方法查詢單例beanreturn ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}if (args != null) {//委派父級IOC 容器根據指定名稱和顯式的參數查找return parentBeanFactory.getBean(nameToLookup, args);}if (requiredType != null) {//委派父級IOC 容器根據指定名稱和類型去查找return parentBeanFactory.getBean(nameToLookup, requiredType);}//委派父級IOC 容器根據指定名稱去查找return parentBeanFactory.getBean(nameToLookup);}//bean 是否需要做類型驗證 一般不需要if (!typeCheckOnly) {//不需要做類型檢查的時候 標記知道beanName 已經在創建中this.markBeanAsCreated(beanName);}try {//根據指定 beanName 合并 beanDefinition 主要是處理子類 bean 繼承父類 bean 的公共屬性問題RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);this.checkMergedBeanDefinition(mbd, beanName, args);//獲取當前bean 的所有依賴bean 名稱String[] dependsOn = mbd.getDependsOn();String[] var11;//當前bean 依賴的bean 是否為空 不為空 優先創建依賴beanif (dependsOn != null) {var11 = dependsOn;int var12 = dependsOn.length;for(int var13 = 0; var13 < var12; ++var13) {String dep = var11[var13];//是否存在循環依賴 存在就報異常if (this.isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}//注冊各個bean 之間的關系 方便bean 銷毀this.registerDependentBean(dep, beanName);try {//創建依賴的beanthis.getBean(dep);} catch (NoSuchBeanDefinitionException var24) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);}}}//是否是單例beanif (mbd.isSingleton()) {//創建bean 對象 并注冊給所有依賴的bean 加入 Spring 一級緩存 并從二級 三級緩存中刪除sharedInstance = this.getSingleton(beanName, () -> {try {//創建單例bean createBean 方法是重點方法return this.createBean(beanName, mbd, args);} catch (BeansException var5) {//創建失敗顯式地從單例緩存中刪除實例this.destroySingleton(beanName);throw var5;}});//獲取指定的bean實例對象bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {//原型bean 每次都會創建一個新的對象var11 = null;Object prototypeInstance;try {//注冊當前創建的原型bean 對象this.beforePrototypeCreation(beanName);//創建bean createBean 方法是重點方法prototypeInstance = this.createBean(beanName, mbd, args);} finally {//異常回調 告訴容器 指定的bean 不再創建this.afterPrototypeCreation(beanName);}//獲取指定的bean實例對象bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else {//既不是單例bean 也不是原型bean 根據bean 定義的作用域來創建bean//獲取bean的作用域(一般分為單例、原型、request、session、application、)String scopeName = mbd.getScope();//獲取指定 bean 的作用域Scope scope = (Scope)this.scopes.get(scopeName);if (scope == null) {//bean 作用域為空 拋出異常throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {//匿名內部類 獲取一個指定作用域的 beanObject scopedInstance = scope.get(beanName, () -> {//注冊當前創建的bean 對象this.beforePrototypeCreation(beanName);Object var4;try {//創建bean createBean 方法是重點方法var4 = this.createBean(beanName, mbd, args);} finally {//異常回調 告訴容器 指定的bean 不再創建this.afterPrototypeCreation(beanName);}return var4;});//獲取指定的bean實例對象bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException var23) {throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);}}} catch (BeansException var26) {//bean 創建失敗后的清理bean 工作this.cleanupAfterBeanCreationFailure(beanName);throw var26;}}//檢查bean 的類型//類型不為空 且bean 不是當前類型if (requiredType != null && !requiredType.isInstance(bean)) {try {//獲取bean 的類型轉換器 并將類型轉換為 requiredTypeT convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());} else {return convertedBean;}} catch (TypeMismatchException var25) {if (this.logger.isTraceEnabled()) {this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}} else {return bean;}
}

DefaultSingletonBeanRegistry#getSingleton 方法源碼分析

DefaultSingletonBeanRegistry#getSingleton 方法主要是依次從 Spring 的一、二、三級緩存中獲取 Bean 對象,需要注意的是容器啟動時候第一次調用 doGetBean 的時候,從 Spring 的一、二、三級緩存中是獲取不到 Bean 對象的,
我們繼續往下看 this.createBean(beanName, mbd, args) 方法中的 this.doCreateBean(beanName, mbdToUse, args) 方法,doCreateBean 方法真正干活的方法。

//從 Spring IOC 容器中獲取單例 bean 方法包裝調用  沒有實際操作
@Nullable
public Object getSingleton(String beanName) {return this.getSingleton(beanName, true);
}//從 Spring 三級緩存中獲取單例 bean
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {//從一級緩存中獲取 beanObject singletonObject = this.singletonObjects.get(beanName);//如果一級緩存中獲取的bean 為空 且bean 正在創建中if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {//加同步鎖synchronized(this.singletonObjects) {//從二級緩存中獲取 beansingletonObject = this.earlySingletonObjects.get(beanName);//二級緩存中bean為空 且bean 允許早期引用if (singletonObject == null && allowEarlyReference) {//從三級緩存中獲取bean(是lambda表達式)ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {//三級緩存中獲取的bean 不為空 執行lambda 表達式 得到半成品的 beansingletonObject = singletonFactory.getObject();//將半成品的bean 放進二級緩存this.earlySingletonObjects.put(beanName, singletonObject);//將三級緩存的bean移出this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

AbstractAutowireCapableBeanFactory#doCreateBean方法源碼分析

doCreateBean 在 Spring Bean 生命周期中分析過,本次重點關注 DefaultSingletonBeanRegistry#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)、AbstractAutowireCapableBeanFactory#populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)和
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 方法。

//AbstractAutowireCapableBeanFactory#doCreateBean
//真正創建Bean的方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {//實例bean 的包裝對象 beanWrapper 其實就是對bean 的包裝BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {//如果是單例bean 從 factoryBean 的緩存中移除當前bean (有可能在本Bean創建之前 就有其他Bean把當前Bean給創建出來了 比如依賴注入過程中)instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//根據beanName、bean的定義信息、args創建一個新的實例對象  根據指定bean使用對應的策略創建新的實例 工廠方法 構造函數自動注入 簡單實例化  底層使用 cglib代理 和 反射生成實例對象instanceWrapper = this.createBeanInstance(beanName, mbd, args);}//獲取 instanceWrapper 包裝的 bean 實例Object bean = instanceWrapper.getWrappedInstance();//獲取 instanceWrapper 包裝的 classClass<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}//加鎖 保證線程安全synchronized(mbd.postProcessingLock) {if (!mbd.postProcessed) {try {//允許后置處理器修改合并后的 beanDefinition 將 工廠中的所有 MergedBeanDefinitionPostProcessors 應用到mbd  調用這些后處理器的 postProcessMergedBeanDefinition 方法this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);} catch (Throwable var17) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);}//標記該 beanDefinition 已經應用過 工廠中的所有 MergedBeanDefinitionPostProcessorsmbd.postProcessed = true;}}//判斷是否需要暴露早期單例對象(解決循環依賴問題)//單例對象 允許循環引用 單例對象正在創建中 滿足三個條件 則需要暴露早期單例對象boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);if (earlySingletonExposure) {//需要暴露早期單例對象if (this.logger.isTraceEnabled()) {this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");}//這里創建了一個匿名的ObjectFactory 工廠 可以用來獲取對象//addSingletonFactory 方法會將這個 匿名的ObjectFactory 工廠 放入到 singletonFactories 中 singletonFactories 是 Spring 的三級緩存//為了避免循環依賴 盡早的持有對象的引用//本次重點關注的方法 三級緩存this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});}//Bean對象的初始化 依賴注入在此觸發//這個exposedObject在初始化完成之后返回作為依賴注入完成后的BeanObject exposedObject = bean;try {//bean 的屬性填充 將bean 對象的實例封裝 將Bean定義中配置的屬性值賦值給實例對象 包括依賴的 bean this.populateBean(beanName, mbd, instanceWrapper);//bean 的初始化 包括應用工廠回調以及init方法和BeanPostProcessorsexposedObject = this.initializeBean(beanName, exposedObject, mbd);} catch (Throwable var18) {if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {throw (BeanCreationException)var18;}throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);}//第二次解決循環依賴問題if (earlySingletonExposure) {//需要暴露早期對象//從一級 二級緩存中獲取bean 對象//把對象加入到二級緩存中本次重點分析的方法Object earlySingletonReference = this.getSingleton(beanName, false);if (earlySingletonReference != null) {//bean 對象不為空 根據名稱獲取的已注冊的Bean和正在實例化的Bean是同一個if (exposedObject == bean) {//當前實例化的Bean初始化完成exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {//當前Bean依賴其他Bean 并且當發生循環引用時不允許新創建實例對象//獲取當前bean 依賴的其他beanString[] dependentBeans = this.getDependentBeans(beanName);//實際依賴的beanSet<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);String[] var12 = dependentBeans;int var13 = dependentBeans.length;//遍歷依賴的beanfor(int var14 = 0; var14 < var13; ++var14) {String dependentBean = var12[var14];//刪除給定Bean名稱的單例實例 但僅當它沒有用于類型檢查之外的其他目的時才刪除if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {//沒有刪除的bean 加入到實際依賴的bean 中actualDependentBeans.add(dependentBean);}}//因為bean創建后其所依賴的bean一定是已經創建的 actualDependentBeans不為空則表示當前的bean創建后其依賴的bean卻沒有全部創建完 說明勛在循環依賴if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}try {//注冊為一次性的bean 也就是處理Bean銷毀前要執行的方法this.registerDisposableBeanIfNecessary(beanName, bean, mbd);return exposedObject;} catch (BeanDefinitionValidationException var16) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);}
}

DefaultSingletonBeanRegistry#addSingletonFactory 方法源碼分析

DefaultSingletonBeanRegistry#addSingletonFactory 方法只做了一件事,就是把早期 Bean 對象放入三級緩存。

//添加單例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)) {//三級緩存中不存在 bean 對象 bean 對象加入三級緩存 this.singletonFactories.put(beanName, singletonFactory);//從二級緩存中移出 bean 對象this.earlySingletonObjects.remove(beanName);//注冊單例bean this.registeredSingletons.add(beanName);}}
}

AbstractAutowireCapableBeanFactory#populateBean方法源碼分析

populateBean 方法就是給對象屬性賦值,在 Spring Bean 生命周期中也分析過,本次我們主要分析 populateBean 方法中的 this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs) 這行代碼,也就是 AbstractAutowireCapableBeanFactory#applyPropertyValues 方法。

//AbstractAutowireCapableBeanFactory#populateBean
//將bean 屬性設置到生成的實例對象上
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {//BeanWrapper 為空 且有依賴的屬性 直接拋異常if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}} else {//在設置屬性之前 給任何InstantiationAwareBeanPostProcessors一個修改bean狀態的機會if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {Iterator var4 = this.getBeanPostProcessorCache().instantiationAware.iterator();while(var4.hasNext()) {InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var4.next();if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}//獲取容器在解析Bean定義資源時為BeanDefiniton中設置的屬性值PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;//依賴注入的邏輯int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == 1 || resolvedAutowireMode == 2) {//MutablePropertyValues是PropertyValues具體的實現類MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);if (resolvedAutowireMode == 1) {//ByNamethis.autowireByName(beanName, mbd, bw, newPvs);}if (resolvedAutowireMode == 2) {//ByTypethis.autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}// 獲取是否有實現InstantiationAwareBeanPostProcessor的方法 這里主要使用postProcessProperties方法。boolean hasInstAwareBpps = this.hasInstantiationAwareBeanPostProcessors();// 獲取是否需要進行依賴注入boolean needsDepCheck = mbd.getDependencyCheck() != 0;PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {// 屬性為空 從beanDefinition中獲取需要注入的信息pvs = mbd.getPropertyValues();}PropertyValues pvsToUse;for(Iterator var9 = this.getBeanPostProcessorCache().instantiationAware.iterator(); var9.hasNext(); pvs = pvsToUse) {//這里會調用AutowiredAnnotationBeanPostProcessor的 postProcessProperties()方法 會直接給對象中的屬性賦值 真正的處理@Autowired @Resource @Value 等注解//AutowiredAnnotationBeanPostProcessor內部并不會處理pvs。直接返回自己設置的PropertyValues對象(可以在實例化前設置)。InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var9.next();//這里的含義是程序員自己已經給屬性賦值了,就不需要Spring給他賦值。這里是程序員沒有賦值,所以需要Spring進行賦值pvsToUse = bp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}//設置值pvsToUse = bp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}}}//是否需要屬性注入if (needsDepCheck) {if (filteredPds == null) {//為空 則初始化filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}//檢查依賴this.checkDependencies(beanName, mbd, filteredPds, (PropertyValues)pvs);}if (pvs != null) {//應用給定的屬性值 解決任何在這個bean工廠運行時其他bean的引用 必須使用深拷貝 所以我們不會永久地修改這個屬性this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);}}
}

AbstractAutowireCapableBeanFactory#applyPropertyValues 方法源碼分析

AbstractAutowireCapableBeanFactory#applyPropertyValues 方法很長,作用是應用給定的屬性值,解決任何在這個 bean 工廠運行時其他bean的引用必須使用深拷貝,所以我們不會永久地修改這個屬性,關于這些功能我們不需要要關注那么多,重點關注 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue) 這行代碼,我們接著看。

//應用給定的屬性值 解決任何在這個bean工廠運行時其他bean的引用 必須使用深拷貝 所以我們不會永久地修改這個屬性
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {//PropertyValues 是否有屬性判斷 沒有屬性不處理if (!pvs.isEmpty()) {//如果有安全控制 且 bw 是 BeanWrapperImpl 的實例if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {//設置 BW 安全上下文為訪問控制山下文((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());}//MutablePropertyValues 是 PropertyValues接口的默認實現 允許對屬性進行簡單操作 并提供構造函數來支持從映射 進行深度復制和構造等MutablePropertyValues mpvs = null;//原始屬性值列表List original;//判斷 pvs 是否是 MutablePropertyValuesif (pvs instanceof MutablePropertyValues) {//是就強轉mpvs = (MutablePropertyValues)pvs;//mpvs 是否需要轉換if (mpvs.isConverted()) {try {//設置屬性值bw.setPropertyValues(mpvs);return;} catch (BeansException var18) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var18);}}//獲取 mpvs 屬性列表original = mpvs.getPropertyValueList();} else {//獲取 pvs 屬性列表original = Arrays.asList(pvs.getPropertyValues());}//獲取工廠自定義類型轉換器TypeConverter converter = this.getCustomTypeConverter();if (converter == null) {converter = bw;}//BeanDefinition 解析BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, (TypeConverter)converter);//創建一個深拷貝List<PropertyValue> deepCopy = new ArrayList(original.size());//是否需要解析標志boolean resolveNecessary = false;//迭代遍歷Iterator var11 = original.iterator();while(true) {while(var11.hasNext()) {//獲取 PropertyValuePropertyValue pv = (PropertyValue)var11.next();if (pv.isConverted()) {//已經轉換 直接加入到 深拷貝中deepCopy.add(pv);} else {//獲取 pv 屬性名稱String propertyName = pv.getName();//獲取 pv 屬性值Object originalValue = pv.getValue();//是否 自動生成標記的規范實例if (originalValue == AutowiredPropertyMarker.INSTANCE) {//獲取寫方法(set)Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();if (writeMethod == null) {//寫方法為空 拋出異常throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);}//將 writeMethod 封裝到 DependencyDescriptor 中originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);}// valueResolver 根據 pv 解析出 originalValue 所封裝的對象 重點方法(循環依賴的對象從這里進去)Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);//解析出來的值Object convertedValue = resolvedValue;//可轉換標記 propertyName 是否 在bw中的可寫屬性 //prepertyName不是表示索引屬性或嵌套屬性boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);//是否可轉換if (convertible) {//將 resolvedValue 轉換為指定的目標屬性對象convertedValue = this.convertForProperty(resolvedValue, propertyName, bw, (TypeConverter)converter);}//resolvedValue 與 originalValue 是否同一個對象if (resolvedValue == originalValue) {//是否可轉換if (convertible) {// convertedValue 設置 給 PVpv.setConvertedValue(convertedValue);}//加入到 deepCopy 中deepCopy.add(pv);} else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue)originalValue).isDynamic() && !(convertedValue instanceof Collection) && !ObjectUtils.isArray(convertedValue)) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);} else {//標記為還需要解析resolveNecessary = true;//構建對象 加入到 deepCopy 中deepCopy.add(new PropertyValue(pv, convertedValue));}}}if (mpvs != null && !resolveNecessary) {//mpvs 不為空 且 不需要再解析 	將此 holder 標記為只包含轉換后的值mpvs.setConverted();}try {//使用 deepCopy 構造一個新的 MutablePropertyValues 對象 設置到 bw 中以對 bw 的屬性值更新bw.setPropertyValues(new MutablePropertyValues(deepCopy));return;} catch (BeansException var19) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var19);}}}
}

BeanDefinitionValueResolver#resolveValueIfNecessary 方法源碼分析

BeanDefinitionValueResolver#resolveValueIfNecessary 方法主要是對參數進行判斷后繼續調用方法。

//給定一個PropertyValue 返回一個值 必要時解析對工廠中其他bean的引用
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {//是否是運行是 bean 引用if (value instanceof RuntimeBeanReference) {//循環依賴是運行時 bean 引用  循環依賴分析的重點RuntimeBeanReference ref = (RuntimeBeanReference)value;return this.resolveReference(argName, ref);} else if (value instanceof RuntimeBeanNameReference) {String refName = ((RuntimeBeanNameReference)value).getBeanName();refName = String.valueOf(this.doEvaluate(refName));if (!this.beanFactory.containsBean(refName)) {throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);} else {return refName;}} else if (value instanceof BeanDefinitionHolder) {BeanDefinitionHolder bdHolder = (BeanDefinitionHolder)value;return this.resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());} else if (value instanceof BeanDefinition) {BeanDefinition bd = (BeanDefinition)value;String innerBeanName = "(inner bean)#" + ObjectUtils.getIdentityHexString(bd);return this.resolveInnerBean(argName, innerBeanName, bd);} else {//非重點 不分析}
}

BeanDefinitionValueResolver#resolveReference 方法源碼分析

BeanDefinitionValueResolver#resolveReference 讓我們看到熟悉的 getBean 方法,如果 Bean A 依賴了 Bean B,而 Bean B 又依賴了 Bean A,假設 Bean A 先創建,那這里的 getBean 方法就是去獲取 Bean B,也就是重新走一遍 Bean A 的流程,循環依賴的循環來了。

//解析應用的屬性值
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {try {//獲取 bean 類型Class<?> beanType = ref.getBeanType();Object bean;//引用對象是否在父容器中 在父容器中 就從父容器獲取對象if (ref.isToParent()) {BeanFactory parent = this.beanFactory.getParentBeanFactory();if (parent == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean " + ref + " in parent factory: no parent factory available");}if (beanType != null) {bean = parent.getBean(beanType);} else {bean = parent.getBean(String.valueOf(this.doEvaluate(ref.getBeanName())));}} else {//不在父容器中String resolvedName;if (beanType != null) {NamedBeanHolder<?> namedBean = this.beanFactory.resolveNamedBean(beanType);bean = namedBean.getBeanInstance();resolvedName = namedBean.getBeanName();} else {//獲取 resolvedName 引用包裝的 bean 名稱resolvedName = String.valueOf(this.doEvaluate(ref.getBeanName()));//獲取 resolvedName 的bean 對象--此處重點來了 又看到 getBean 方法了 熟悉的感覺來了bean = this.beanFactory.getBean(resolvedName);}this.beanFactory.registerDependentBean(resolvedName, this.beanName);}if (bean instanceof NullBean) {bean = null;}return bean;} catch (BeansException var7) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, var7);}
}

DefaultSingletonBeanRegistry#getSingleton方法源碼分析

DefaultSingletonBeanRegistry#getSingleton方法主要就是從三級緩存獲取到對象,執行 lambda 表達式,并把得到的對象放入到二級緩存中,同時刪除三級緩存的對象,DefaultSingletonBeanRegistry#getSingleton 方法中獲取的 bean 對象和 DefaultSingletonBeanRegistry#addSingletonFactory 方法中的 Bean 對象的區別是兩個方法之間是調用了 populateBean 方法的,也就是精力屬性賦值,對象的屬性值是有變化的。

//從緩存中獲取 bean 對象
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {//從一級緩存中獲取對象Object singletonObject = this.singletonObjects.get(beanName);//一級緩緩存中沒有獲取到對象且單例對象正在創建中if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {//同步防止線程安全問題synchronized(this.singletonObjects) {//從二級緩存中獲取對象singletonObject = this.earlySingletonObjects.get(beanName);//二級緩存中對象為空且bean 對象允許暴露早期引用if (singletonObject == null && allowEarlyReference) {//從三級緩存中獲取bean 對象(獲取到的是lambda表達式)ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {//執行lambda表達式 也是回調 獲取到 bean 對象singletonObject = singletonFactory.getObject();//bean 對象保存到二級緩存中(半成品對象)--這里的對象是經過了 populateBean 屬性賦值的this.earlySingletonObjects.put(beanName, singletonObject);//從三級緩存中移出早期對象this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

至此,Spring 三級緩存相關源碼已經分析完畢,希望可以幫助到有需要的小伙伴。

歡迎提出建議及對錯誤的地方指出糾正。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13213.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13213.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13213.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

三生隨記——麗水詭事

在浙江的深山之中&#xff0c;隱藏著一座名為麗水的古老小城。這里山水秀麗&#xff0c;風景如畫&#xff0c;但在這美麗的外表下&#xff0c;卻隱藏著不為人知的恐怖秘密。 傳聞&#xff0c;麗水的郊外有一片被詛咒的竹林。這片竹林與其他竹林不同&#xff0c;它的葉子常年枯黃…

c# datagridview基本操作,包括行拖拽,添加自定義行列。

項目場景&#xff1a; 這段代碼定義了一個名為 ucDatagridviewHelper 的用戶控件&#xff08;UserControl&#xff09;&#xff0c;該控件包含了一個 DataGridView 控件和一些其他功能。 這段代碼的主要部分&#xff1a; 構造函數&#xff1a;在構造函數中&#xff0c;初始化…

C++ | Leetcode C++題解之第89題格雷編碼

題目&#xff1a; 題解&#xff1a; class Solution { public:vector<int> grayCode(int n) {vector<int> ret(1 << n);for (int i 0; i < ret.size(); i) {ret[i] (i >> 1) ^ i;}return ret;} };

數據結構--紅黑樹(RBTree)

一、紅黑樹概念 1.1 什么是紅黑樹 紅黑樹&#xff0c;是一種二叉搜索樹&#xff0c;但在每個結點上增加一個存儲位表示結點的顏色&#xff0c;可以是Red或 Black。 通過對任何一條從根到葉子的路徑上各個結點著色方式的限制&#xff0c;紅黑樹確保沒有一條路徑會比其他路徑長…

openEuler-22.03-LTS安裝opengauss5.0.1(包含cm集群管理)主備

環境說明 openEuler-22.0.3-LTS opengauss5.0.1 安裝數據庫 安裝系統依賴包 yum -y install lksctp* yum -y install psmisc yum -y install bzip2 yum -y install unzip yum -y install gcc yum -y install gcc-c yum -y install perl yum -y install libxml2-devel yum …

前端(包含cocosCreator)開發環節調取后端接口時跨域,解決辦法之反向代理

/** eslint-disable */ var http require(http),httpProxy require(http-proxy),HttpProxyRules require(http-proxy-rules);// Set up proxy rules instance var port 9090 var proxyRules new HttpProxyRules({rules: {/api/(.*): https://baidu.com/$1, // 測試環境游戲…

自學VBA 設置單元格文字格式 筆記

一.設定對應單元格對應需要顯示的格式 Cells(1, 1).Font.Size 18 字體大小 Cells(1, 2).Font.Color RGB(255, 0, 0) 字體顏色 Cells(1, 3).Font.Name "黑體" 字體類型 Cells(1, 4).Font.Italic True 字體斜體 Cells(1, 5).Font.FontStyle "BOLD"…

ubuntu下gcc編譯器的安裝

.gcc編譯器的安裝 一般linux下是覆蓋含有的&#xff0c;如果沒有執行更新命令 sudo apt update gcc安裝成功&#xff0c;可以檢查一下版本 可以看出我的gcc是9.4.0版本的

驗證torch.nn.Conv2d

import os import cv2 import torch import numpy as np import random import cv2 as cv from matplotlib import pyplot as pltdef f_VerifyConv2D():"""驗證torch.nn.Conv2d&#xff0c; 并將輸入數據及權重保存到txt文件中"""x torch.randn…

SpringBoot環境隔離Profiles

前言 通常我們開發不可能只有一個生產環境&#xff0c;還會有其它的開發&#xff0c;測試&#xff0c;預發布環境等等。為了更好的管理每個環境的配置項&#xff0c;springboot也提供了對應的環境隔離的方法。 直接上干貨 知識點 激活環境方法 1&#xff0c;在application…

專用設備制造業供應商收發文件,有什么專業而輕便的方式嗎?

專用設備制造業的特點包括&#xff1a;門類廣、跨度大、科技含量高。它主要生產的是國民經濟各部門&#xff08;包括采掘、化工、冶煉、能源、醫療衛生、環保等&#xff09;所急需的重大成套設備&#xff0c;例如礦產資源井采及露天開采設備、大型火電、水電、核電成套設備、石…

教育行業文本短信VS視頻短信VS語音短信哪個好?

在教育行業中&#xff0c;文本短信、視頻短信和語音短信各有其優勢&#xff0c;選擇哪種方式更好取決于具體的應用場景和目標。 文本短信的優勢在于&#xff1a; 1.簡潔明了&#xff1a;能夠快速、直接地傳遞信息&#xff0c;對于需要快速通知或提醒的場景非常適用。 …

通過內網穿透免費部署我們的springboot+vue項目 實現跟服務器一樣的效果

前文講到通過內網穿透能夠實現遠程訪問個人電腦的靜態資源。本文將講解通過內網穿透實現遠程訪問本地的項目&#xff0c;實現跟部署到服務器一樣的效果&#xff1a;前文鏈接&#xff1a;通過內網穿透實現遠程訪問個人電腦資源詳細過程&#xff08;免費&#xff09;&#xff08;…

深度學習之卷積神經網絡理論基礎

深度學習之卷積神經網絡理論基礎 卷積層的操作&#xff08;Convolutional layer&#xff09; 在提出卷積層的概念之前首先引入圖像識別的特點 圖像識別的特點 特征具有局部性&#xff1a;老虎重要特征“王字”僅出現在頭部區域特征可能出現在任何位置下采樣圖像&#xff0c…

Python 小抄

Python 備忘單 目錄 1.語法和空格 2.注釋 3.數字和運算 4.字符串處理 5.列表、元組和字典 6.JSON 7.循環 8.文件處理 9.函數 10.處理日期時間 11.NumPy 12.Pandas 要運行單元格&#xff0c;請按 ShiftEnter 或單擊頁面頂部的 Run&#xff08;運行&#xff09;。 1.語法和空格…

三種方法進行跨服務器文件傳輸

今天需要在一個centOS服務器上編譯一個工具, 我的本地主機是ubuntu, 但是由于服務器是合規環境, 沒有文件傳輸的接口, 也不能訪問github等外網, 所以很多依賴只能下載到ubuntu然后在想辦法搞到服務器上. 這種場景有三種簡單有用的辦法, 整理一下. 方法一: 把主機配置成http ser…

6---Linux下版本控制器Git的知識點

一、Linux之父與Git的故事&#xff1a; Linux之父叫做“Linus Torvalds”&#xff0c;我們簡稱為雷納斯。Linux是開源項目&#xff0c;所以在Linux的早期開發中&#xff0c;許多世界各地的能力各異的程序員都參與到Linux的項目開發中。那時&#xff0c;雷納斯每天都會收到許許…

用ntpdate同步時間出現問題

1. 使用ntpdate同步 [rootnode ~]# ntpdate ntp.aliyun.com4 Aug 00:07:17 ntpdate[20924]: adjust time server 203.107.6.88 offset -0.001543 sec2. 查看時間 [rootnode ~]# date Thu Aug 4 00:07:46 CST 20223. 如果時間對不上 報錯信息 cna02:~ # ntpdate ntp1.aliyu…

mysql社區版最多支持多個連接并發

MySQL社區版對于并發連接數的支持并沒有一個固定的上限&#xff0c;它實際上取決于多個因素&#xff0c;包括服務器的硬件配置、MySQL的配置參數以及應用程序的設計等。 硬件配置&#xff1a;服務器的CPU、內存和磁盤I/O等硬件資源會直接影響MySQL可以處理的并發連接數。例如&a…

VMware Fusion 13.5.2 for Mac 發布,產品訂閱模式首個重大變更

VMware Fusion 13.5.2 for Mac 發布&#xff0c;產品訂閱模式首個重大變更 適用于基于 Intel 處理器和搭載 Apple 芯片的 Mac 的桌面虛擬化軟件 請訪問原文鏈接&#xff1a;https://sysin.org/blog/vmware-fusion-13/&#xff0c;查看最新版。原創作品&#xff0c;轉載請保留…