摘要:關(guān)于創(chuàng)建實(shí)例的過(guò)程,我將會(huì)分幾篇文章進(jìn)行分析。源碼分析創(chuàng)建實(shí)例的入口在正式分析方法前,我們先來(lái)看看方法是在哪里被調(diào)用的。時(shí),表明方法不存在,此時(shí)拋出異常。該變量用于表示是否提前暴露單例,用于解決循環(huán)依賴。
1. 簡(jiǎn)介
在上一篇文章中,我比較詳細(xì)的分析了獲取 bean 的方法,也就是getBean(String)的實(shí)現(xiàn)邏輯。對(duì)于已實(shí)例化好的單例 bean,getBean(String) 方法并不會(huì)再一次去創(chuàng)建,而是從緩存中獲取。如果某個(gè) bean 還未實(shí)例化,這個(gè)時(shí)候就無(wú)法命中緩存。此時(shí),就要根據(jù) bean 的配置信息去創(chuàng)建這個(gè) bean 了。相較于getBean(String)方法的實(shí)現(xiàn)邏輯,創(chuàng)建 bean 的方法createBean(String, RootBeanDefinition, Object[])及其所調(diào)用的方法邏輯上更為復(fù)雜一些。關(guān)于創(chuàng)建 bean 實(shí)例的過(guò)程,我將會(huì)分幾篇文章進(jìn)行分析。本篇文章會(huì)先從大體上分析 createBean(String, RootBeanDefinition, Object[])方法的代碼邏輯,至于其所調(diào)用的方法將會(huì)在隨后的文章中進(jìn)行分析。
好了,其他的不多說(shuō),直接進(jìn)入正題吧。
2. 源碼分析 2.1 創(chuàng)建 bean 實(shí)例的入口在正式分析createBean(String, RootBeanDefinition, Object[])方法前,我們先來(lái)看看 createBean 方法是在哪里被調(diào)用的。如下:
public T doGetBean(...) { // 省略不相關(guān)代碼 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory
上面是 doGetBean 方法的代碼片段,從中可以發(fā)現(xiàn) createBean 方法。createBean 方法被匿名工廠類的 getObject 方法包裹,但這個(gè)匿名工廠類對(duì)象并未直接調(diào)用 getObject 方法。而是將自身作為參數(shù)傳給了getSingleton(String, ObjectFactory)方法,那么我們接下來(lái)再去看看一下getSingleton(String, ObjectFactory) 方法的實(shí)現(xiàn)。如下:
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) { 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!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean "" + beanName + """); } /* * 將 beanName 添加到 singletonsCurrentlyInCreation 集合中, * 用于表明 beanName 對(duì)應(yīng)的 bean 正在創(chuàng)建中 */ beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { // 通過(guò) getObject 方法調(diào)用 createBean 方法創(chuàng)建 bean 實(shí)例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // 將 beanName 從 singletonsCurrentlyInCreation 移除 afterSingletonCreation(beanName); } if (newSingleton) { /* * 將 鍵值對(duì)添加到 singletonObjects 集合中, * 并從其他集合(比如 earlySingletonObjects)中移除 singletonObject 記錄 */ addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
上面的方法邏輯不是很復(fù)雜,這里簡(jiǎn)單總結(jié)一下。如下:
先從 singletonObjects 集合獲取 bean 實(shí)例,若不為空,則直接返回
若為空,進(jìn)入創(chuàng)建 bean 實(shí)例階段。先將 beanName 添加到 singletonsCurrentlyInCreation
通過(guò) getObject 方法調(diào)用 createBean 方法創(chuàng)建 bean 實(shí)例
將 beanName 從 singletonsCurrentlyInCreation 集合中移除
將
從上面的分析中,我們知道了 createBean 方法在何處被調(diào)用的。那么接下來(lái)我們一起深入 createBean 方法的源碼中,來(lái)看看這個(gè)方法具體都做了什么事情。
2.2 createBean 方法全貌createBean 和 getBean 方法類似,基本上都是空殼方法,只不過(guò) createBean 的邏輯稍微多點(diǎn),多做了一些事情。下面我們一起看看這個(gè)方法的實(shí)現(xiàn)邏輯,如下:
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean "" + beanName + """); } RootBeanDefinition mbdToUse = mbd; // 解析 bean 的類型 Class> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } try { // 處理 lookup-method 和 replace-method 配置,Spring 將這兩個(gè)配置統(tǒng)稱為 override method mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 在 bean 初始化前應(yīng)用后置處理,如果后置處理返回的 bean 不為空,則直接返回 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } // 調(diào)用 doCreateBean 創(chuàng)建 bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean "" + beanName + """); } return beanInstance; }
上面的代碼不長(zhǎng),代碼的執(zhí)行流程比較容易看出,這里羅列一下:
解析 bean 類型
處理 lookup-method 和 replace-method 配置
在 bean 初始化前應(yīng)用后置處理,若后置處理返回的 bean 不為空,則直接返回
若上一步后置處理返回的 bean 為空,則調(diào)用 doCreateBean 創(chuàng)建 bean 實(shí)例
下面我會(huì)分節(jié)對(duì)第2、3和4步的流程進(jìn)行分析,步驟1的詳細(xì)實(shí)現(xiàn)大家有興趣的話,就自己去看看吧。
2.2.1 驗(yàn)證和準(zhǔn)備 override 方法當(dāng)用戶配置了 lookup-method 和 replace-method 時(shí),Spring 需要對(duì)目標(biāo) bean 進(jìn)行增強(qiáng)。在增強(qiáng)之前,需要做一些準(zhǔn)備工作,也就是 prepareMethodOverrides 中的邏輯。下面來(lái)看看這個(gè)方法的源碼:
public void prepareMethodOverrides() throws BeanDefinitionValidationException { MethodOverrides methodOverrides = getMethodOverrides(); if (!methodOverrides.isEmpty()) { Setoverrides = methodOverrides.getOverrides(); synchronized (overrides) { // 循環(huán)處理每個(gè) MethodOverride 對(duì)象 for (MethodOverride mo : overrides) { prepareMethodOverride(mo); } } } } protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { // 獲取方法名為 mo.getMethodName() 的方法數(shù)量,當(dāng)方法重載時(shí),count 的值就會(huì)大于1 int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); // count = 0,表明根據(jù)方法名未找到相應(yīng)的方法,此時(shí)拋出異常 if (count == 0) { throw new BeanDefinitionValidationException( "Invalid method override: no method with name "" + mo.getMethodName() + "" on class [" + getBeanClassName() + "]"); } // 若 count = 1,表明僅存在已方法名為 mo.getMethodName(),這意味著方法不存在重載 else if (count == 1) { // 方法不存在重載,則將 overloaded 成員變量設(shè)為 false mo.setOverloaded(false); } }
上面的源碼中,prepareMethodOverrides方法循環(huán)調(diào)用了prepareMethodOverride方法,并沒(méi)其他的太多邏輯。主要準(zhǔn)備工作都是在 prepareMethodOverride 方法中進(jìn)行的,所以我們重點(diǎn)關(guān)注一下這個(gè)方法。prepareMethodOverride 這個(gè)方法主要用于獲取指定方法的方法數(shù)量 count,并根據(jù) count 的值進(jìn)行相應(yīng)的處理。count = 0 時(shí),表明方法不存在,此時(shí)拋出異常。count = 1 時(shí),設(shè)置 MethodOverride 對(duì)象的overloaded成員變量為 false。這樣做的目的在于,提前標(biāo)注名稱mo.getMethodName()的方法不存在重載,在使用 CGLIB 增強(qiáng)階段就不需要進(jìn)行校驗(yàn),直接找到某個(gè)方法進(jìn)行增強(qiáng)即可。
上面的方法沒(méi)太多的邏輯,比較簡(jiǎn)單,就先分析到這里。
2.2.2 bean 實(shí)例化前的后置處理后置處理是 Spring 的一個(gè)拓展點(diǎn),用戶通過(guò)實(shí)現(xiàn) BeanPostProcessor 接口,并將實(shí)現(xiàn)類配置到 Spring 的配置文件中(或者使用注解),即可在 bean 初始化前后進(jìn)行自定義操作。關(guān)于后置處理較為詳細(xì)的說(shuō)明,可以參考我的了一篇文章Spring IOC 容器源碼分析系列文章導(dǎo)讀,這里就不贅述了。下面我們來(lái)看看 createBean 方法中的后置處理邏輯,如下:
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; // 檢測(cè)是否解析過(guò),mbd.beforeInstantiationResolved 的值在下面的代碼中會(huì)被設(shè)置 if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class> targetType = determineTargetType(beanName, mbd); if (targetType != null) { // 應(yīng)用前置處理 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { // 應(yīng)用后置處理 bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } // 設(shè)置 mbd.beforeInstantiationResolved mbd.beforeInstantiationResolved = (bean != null); } return bean; } protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { // InstantiationAwareBeanPostProcessor 一般在 Spring 框架內(nèi)部使用,不建議用戶直接使用 if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // bean 初始化前置處理 Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; } public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { // bean 初始化后置處理 result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
在 resolveBeforeInstantiation 方法中,當(dāng)前置處理方法返回的 bean 不為空時(shí),后置處理才會(huì)被執(zhí)行。前置處理器是 InstantiationAwareBeanPostProcessor 類型的,該種類型的處理器一般用在 Spring 框架內(nèi)部,比如 AOP 模塊中的AbstractAutoProxyCreator抽象類間接實(shí)現(xiàn)了這個(gè)接口中的 postProcessBeforeInstantiation方法,所以 AOP 可以在這個(gè)方法中生成為目標(biāo)類的代理對(duì)象。不過(guò)我在調(diào)試的過(guò)程中,發(fā)現(xiàn) AOP 在此處生成代理對(duì)象是有條件的。一般情況下條件都不成立,也就不會(huì)在此處生成代理對(duì)象。至于這個(gè)條件為什么不成立,因 AOP 這一塊的源碼我還沒(méi)來(lái)得及看,所以暫時(shí)還無(wú)法解答。等我看過(guò) AOP 模塊的源碼后,我再來(lái)嘗試分析這個(gè)條件。
2.2.3 調(diào)用 doCreateBean 方法創(chuàng)建 bean這一節(jié),我們來(lái)分析一下doCreateBean方法的源碼。在 Spring 中,做事情的方法基本上都是以do開(kāi)頭的,doCreateBean 也不例外。那下面我們就來(lái)看看這個(gè)方法都做了哪些事情。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { /* * BeanWrapper 是一個(gè)基礎(chǔ)接口,由接口名可看出這個(gè)接口的實(shí)現(xiàn)類用于包裹 bean 實(shí)例。 * 通過(guò) BeanWrapper 的實(shí)現(xiàn)類可以方便的設(shè)置/獲取 bean 實(shí)例的屬性 */ BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { // 從緩存中獲取 BeanWrapper,并清理相關(guān)記錄 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /* * 創(chuàng)建 bean 實(shí)例,并將實(shí)例包裹在 BeanWrapper 實(shí)現(xiàn)類對(duì)象中返回。createBeanInstance * 中包含三種創(chuàng)建 bean 實(shí)例的方式: * 1. 通過(guò)工廠方法創(chuàng)建 bean 實(shí)例 * 2. 通過(guò)構(gòu)造方法自動(dòng)注入(autowire by constructor)的方式創(chuàng)建 bean 實(shí)例 * 3. 通過(guò)無(wú)參構(gòu)造方法方法創(chuàng)建 bean 實(shí)例 * * 若 bean 的配置信息中配置了 lookup-method 和 replace-method,則會(huì)使用 CGLIB * 增強(qiáng) bean 實(shí)例。關(guān)于這個(gè)方法,后面會(huì)專門寫(xiě)一篇文章介紹,這里先說(shuō)這么多。 */ instanceWrapper = createBeanInstance(beanName, mbd, args); } // 此處的 bean 可以認(rèn)為是一個(gè)原始的 bean 實(shí)例,暫未填充屬性 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd.resolvedTargetType = beanType; // 這里又遇到后置處理了,此處的后置處理是用于處理已“合并的 BeanDefinition”。關(guān)于這種后置處理器具體的實(shí)現(xiàn)細(xì)節(jié)就不深入理解了,大家有興趣可以自己去看 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } /* * earlySingletonExposure 是一個(gè)重要的變量,這里要說(shuō)明一下。該變量用于表示是否提前暴露 * 單例 bean,用于解決循環(huán)依賴。earlySingletonExposure 由三個(gè)條件綜合而成,如下: * 條件1:mbd.isSingleton() - 表示 bean 是否是單例類型 * 條件2:allowCircularReferences - 是否允許循環(huán)依賴 * 條件3:isSingletonCurrentlyInCreation(beanName) - 當(dāng)前 bean 是否處于創(chuàng)建的狀態(tài)中 * * earlySingletonExposure = 條件1 && 條件2 && 條件3 * = 單例 && 是否允許循環(huán)依賴 && 是否存于創(chuàng)建狀態(tài)中。 */ boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean "" + beanName + "" to allow for resolving potential circular references"); } // 添加工廠對(duì)象到 singletonFactories 緩存中 addSingletonFactory(beanName, new ObjectFactory
上面的注釋比較多,分析的應(yīng)該比較詳細(xì)的。不過(guò)有一部分代碼我暫時(shí)沒(méi)看懂,就不分析了,見(jiàn)諒。下面我們來(lái)總結(jié)一下 doCreateBean 方法的執(zhí)行流程吧,如下:
從緩存中獲取 BeanWrapper 實(shí)現(xiàn)類對(duì)象,并清理相關(guān)記錄
若未命中緩存,則創(chuàng)建 bean 實(shí)例,并將實(shí)例包裹在 BeanWrapper 實(shí)現(xiàn)類對(duì)象中返回
應(yīng)用 MergedBeanDefinitionPostProcessor 后置處理器相關(guān)邏輯
根據(jù)條件決定是否提前暴露 bean 的早期引用(early reference),用于處理循環(huán)依賴問(wèn)題
調(diào)用 populateBean 方法向 bean 實(shí)例中填充屬性
調(diào)用 initializeBean 方法完成余下的初始化工作
注冊(cè)銷毀邏輯
doCreateBean 方法的流程比較復(fù)雜,步驟略多。由此也可了解到創(chuàng)建一個(gè) bean 還是很復(fù)雜的,這中間要做的事情繁多。比如填充屬性、對(duì) BeanPostProcessor 拓展點(diǎn)提供支持等。以上的步驟對(duì)應(yīng)的方法具體是怎樣實(shí)現(xiàn)的,本篇文章并不打算展開(kāi)分析。在后續(xù)的文章中,我會(huì)多帶帶寫(xiě)文章分析幾個(gè)邏輯比較復(fù)雜的步驟。有興趣的閱讀的朋友可以稍微等待一下,相關(guān)文章本周會(huì)陸續(xù)進(jìn)行更新。
3. 總結(jié)到這里,createBean 方法及其所調(diào)用的方法的源碼就分析完了。總的來(lái)說(shuō),createBean 方法還是比較復(fù)雜的,需要多看幾遍才能理清一些頭緒。由于 createBean 方法比較復(fù)雜,對(duì)于以上的源碼分析,我并不能保證不出錯(cuò)。如果有寫(xiě)錯(cuò)的地方,還請(qǐng)大家指點(diǎn)迷津。畢竟當(dāng)局者迷,作為作者,我很難意識(shí)到哪里寫(xiě)的有問(wèn)題。
好了,本篇文章到此結(jié)束。謝謝閱讀。
參考《Spring 源碼深度解析》- 郝佳
附錄:Spring 源碼分析文章列表 Ⅰ. IOC更新時(shí)間 | 標(biāo)題 |
---|---|
2018-05-30 | Spring IOC 容器源碼分析系列文章導(dǎo)讀 |
2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
2018-06-04 | Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過(guò)程 |
2018-06-06 | Spring IOC 容器源碼分析 - 創(chuàng)建原始 bean 對(duì)象 |
2018-06-08 | Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法 |
2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對(duì)象 |
2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
更新時(shí)間 | 標(biāo)題 |
---|---|
2018-06-17 | Spring AOP 源碼分析系列文章導(dǎo)讀 |
2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
2018-06-20 | Spring AOP 源碼分析 - 創(chuàng)建代理對(duì)象 |
2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過(guò)程 |
更新時(shí)間 | 標(biāo)題 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一個(gè)請(qǐng)求的旅行過(guò)程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的創(chuàng)建過(guò)程 |
本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處
作者:coolblog.xyz
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz
本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際許可協(xié)議進(jìn)行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/69639.html
摘要:初始化我們知道容器初始化后會(huì)對(duì)容器中非懶加載的,單例的以及非抽象的定義進(jìn)行的初始化操作,所以我們分析源碼的入口也就是在容器初始化的入口,分析容器初始化后在什么地方開(kāi)始第一次的初始化。 前言 Spring IOC容器在初始化之后會(huì)對(duì)容器中非懶加載的,單例的以及非抽象的bean定義進(jìn)行bean的初始化操作,同時(shí)會(huì)也涉及到Bean的后置處理器以及DI(依賴注入)等行為。對(duì)于Bean的初始化,...
摘要:簡(jiǎn)介為了寫(xiě)容器源碼分析系列的文章,我特地寫(xiě)了一篇容器的導(dǎo)讀文章。在做完必要的準(zhǔn)備工作后,從本文開(kāi)始,正式開(kāi)始進(jìn)入源碼分析的階段。從緩存中獲取單例。返回以上就是和兩個(gè)方法的分析。 1. 簡(jiǎn)介 為了寫(xiě) Spring IOC 容器源碼分析系列的文章,我特地寫(xiě)了一篇 Spring IOC 容器的導(dǎo)讀文章。在導(dǎo)讀一文中,我介紹了 Spring 的一些特性以及閱讀 Spring 源碼的一些建議。在...
摘要:簡(jiǎn)介本篇文章是容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是方法,該方法用于對(duì)已完成屬性填充的做最后的初始化工作。后置處理器是拓展點(diǎn)之一,通過(guò)實(shí)現(xiàn)后置處理器接口,我們就可以插手的初始化過(guò)程。 1. 簡(jiǎn)介 本篇文章是Spring IOC 容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是 initializeBean 方法,該方法用于對(duì)已完成屬性填充的 bean 做最...
摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過(guò)這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來(lái)了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開(kāi)發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過(guò)十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...
摘要:實(shí)例化時(shí),發(fā)現(xiàn)又依賴于。一些緩存的介紹在進(jìn)行源碼分析前,我們先來(lái)看一組緩存的定義。可是看完源碼后,我們似乎仍然不知道這些源碼是如何解決循環(huán)依賴問(wèn)題的。 1. 簡(jiǎn)介 本文,我們來(lái)看一下 Spring 是如何解決循環(huán)依賴問(wèn)題的。在本篇文章中,我會(huì)首先向大家介紹一下什么是循環(huán)依賴。然后,進(jìn)入源碼分析階段。為了更好的說(shuō)明 Spring 解決循環(huán)依賴的辦法,我將會(huì)從獲取 bean 的方法getB...
閱讀 1534·2023-04-26 02:50
閱讀 3535·2023-04-26 00:28
閱讀 1931·2023-04-25 15:18
閱讀 3209·2021-11-24 10:31
閱讀 986·2019-08-30 13:00
閱讀 1000·2019-08-29 15:19
閱讀 1766·2019-08-29 13:09
閱讀 2975·2019-08-29 13:06