您好,登錄后才能下訂單哦!
這篇文章主要介紹Spring解決循環(huán)依賴的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
這里我們先借用一張圖來(lái)通過(guò)視覺(jué)感受一下,看圖:
其實(shí),通過(guò)上面圖片我想你應(yīng)該能看圖說(shuō)話了,所謂的循環(huán)依賴其實(shí)就是一種死循環(huán)。想象一下生活中的例子就是,你作為一個(gè)猛男喜歡一個(gè)蘿莉,而蘿莉卻愛(ài)上了你的基友娘炮,但是娘炮心理卻一直想著和你去澡堂洗澡時(shí)撿你扔的肥皂。
是的,這就是循環(huán)依賴的本因。當(dāng)spring啟動(dòng)在解析配置創(chuàng)建bean的過(guò)程中。首先在初始化A的時(shí)候發(fā)現(xiàn)需要引用B,然后去初始化B的時(shí)候又發(fā)現(xiàn)引用了C,然后又去初始化C卻發(fā)現(xiàn)一個(gè)操蛋的結(jié)果,C引用了A。它又去初始化A一次循環(huán)無(wú)窮盡,如你們這該死的變態(tài)三角關(guān)系一樣。
既然形成了這種看起來(lái)無(wú)法 解決的三角關(guān)系,那么有什么辦法解決呢?相信聰明的你在面對(duì)這樣尷尬的境地,已經(jīng)開(kāi)始思考解決方案了。想不想的出來(lái)沒(méi)關(guān)系,我們看看spring的大神們是如何來(lái)解決這個(gè)問(wèn)題的。
Spring解決循環(huán)依賴的方法就是如題所述的三級(jí)緩存、預(yù)曝光。
Spring的三級(jí)緩存主要是singletonObjects、earlySingletonObjects、singletonFactories這三個(gè)Map:
代碼 1-1:
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
我們知道了Spring為解決循環(huán)依賴所定義的三級(jí)緩存了,那么我們就來(lái)看看它是如何通過(guò)這三級(jí)緩存來(lái)解決這個(gè)問(wèn)題的。
代碼 1-2:
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); //首先通過(guò)beanName從一級(jí)緩存獲取bean if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { //如果一級(jí)緩存中沒(méi)有,并且beanName映射的bean正在創(chuàng)建中 synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); //從二級(jí)緩存中獲取 if (singletonObject == null && allowEarlyReference) { //二級(jí)緩存也沒(méi)有 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); //從三級(jí)緩存獲取 if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); //獲取到bean this.earlySingletonObjects.put(beanName, singletonObject); //將獲取的bean提升至二級(jí)緩存 this.singletonFactories.remove(beanName); //從三級(jí)緩存刪除 } } } } return singletonObject; }
上面的方法就是Spring獲取single bean的過(guò)程,其中的一些方法的解釋我就直接借用其它博主的文摘了:
isSingletonCurrentlyInCreation()
:判斷當(dāng)前 singleton bean 是否處于創(chuàng)建中。bean 處于創(chuàng)建中也就是說(shuō) bean 在初始化但是沒(méi)有完成初始化,有一個(gè)這樣的過(guò)程其實(shí)和 Spring 解決 bean 循環(huán)依賴的理念相輔相成,因?yàn)?Spring 解決 singleton bean 的核心就在于提前曝光 bean。
allowEarlyReference:從字面意思上面理解就是允許提前拿到引用。其實(shí)真正的意思是是否允許從 singletonFactories 緩存中通過(guò)getObject()
拿到對(duì)象,為什么會(huì)有這樣一個(gè)字段呢?原因就在于 singletonFactories 才是 Spring 解決 singleton bean 的訣竅所在。
好了,說(shuō)道這里我們來(lái)縷清一下當(dāng)我們執(zhí)行下面代碼獲取一個(gè)name為 user 的bean時(shí)Spring都經(jīng)過(guò)了怎樣的過(guò)程。
代碼 1-3:
ClassPathResource resource = new ClassPathResource("bean.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(resource); UserBean user = (UserBean) factory.getBean("user");
通過(guò)追蹤源碼我們發(fā)現(xiàn)getBean()方法執(zhí)行后會(huì)調(diào)用到AbstractBeanFactory.doGetBean()方法,在此方法中調(diào)用了我們上文提到的關(guān)鍵getSingleton()。因?yàn)殚_(kāi)始啟動(dòng)項(xiàng)目,獲取第一個(gè)bean時(shí)緩存都是空的,所以直接返回一個(gè)null。追蹤源碼發(fā)現(xiàn),在doGetBean()后面會(huì)調(diào)用到AbstractAutowireCapableBeanFactory.doCreateBean()方法進(jìn)行bean創(chuàng)建,詳細(xì)流程就自行追蹤源碼。在這個(gè)方法中有一段代碼:
代碼 1-4:
// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); //通過(guò)條件判斷該bean是否允許提前曝露 if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //允許提前暴露的bean 添加到三級(jí)緩存 }
通過(guò)上面的代碼我們發(fā)現(xiàn),可以提前暴露的bean通過(guò)addSingletonFactory()方法添加到了三級(jí)緩存SingletonFactories 中,我們看一下它是怎樣操作的。
代碼 1-5:
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); //添加到三級(jí)緩存 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
此時(shí),我們的user這個(gè)bean就被加入到了三級(jí)緩存中,那么什么樣bean的才會(huì)被加入到三級(jí)緩存中呢?就是代碼 1-4中的三個(gè)判斷條件:
單例
允許循環(huán)引用的bean
當(dāng)前 bean 正在創(chuàng)建中
到這里user這個(gè)bean已經(jīng)創(chuàng)建,但是它還不是一個(gè)完整的bean,還需要后續(xù)的初始化。但是這不影響其它的bean引用它,假設(shè)user為開(kāi)始圖中的A,那么當(dāng)在初始化A(user)的時(shí)候,發(fā)現(xiàn)A中有一個(gè)屬性B,在調(diào)用方法applyPropertyValues()去設(shè)置這個(gè)B的時(shí)候。會(huì)發(fā)現(xiàn)B還沒(méi)創(chuàng)建,Spring就會(huì)在重復(fù)創(chuàng)建A的流程調(diào)用doCreate()來(lái)創(chuàng)建B,然后添加到三級(jí)緩存,設(shè)置B的屬性C時(shí),發(fā)現(xiàn)C也還沒(méi)創(chuàng)建,接著重復(fù)前述doCreate()步驟進(jìn)行C的創(chuàng)建。C創(chuàng)建完成,進(jìn)行初始化發(fā)現(xiàn)C引用了A,這時(shí)關(guān)鍵的地方就是上面的代碼1-2處。
在C設(shè)置屬性A的時(shí)候,調(diào)用getSingleton()獲取bean時(shí),因?yàn)锳已經(jīng)在代碼1-4處添加到了三級(jí)緩存中,C可以直接獲取到A的實(shí)例并設(shè)置成功后,繼續(xù)完成自己創(chuàng)建。初始化完成后,調(diào)用如下方法將自己添加到一級(jí)緩存。
代碼 1-6:
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
以上是“Spring解決循環(huán)依賴的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。