自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Bean IOC、AOP 循環(huán)依賴解讀

開(kāi)發(fā) 架構(gòu)
學(xué)有四年時(shí)間,但幾乎所有人都是臨近畢業(yè)才發(fā)現(xiàn)找一份好工作費(fèi)勁,尤其是我能非常熟悉的軟件開(kāi)發(fā)行業(yè),即使是畢業(yè)了還需要額外花錢(qián)到培訓(xùn)機(jī)構(gòu),在學(xué)一遍編程技術(shù)才能出去找工作。好像在校這幾年壓根就沒(méi)學(xué)到什么!

[[397413]]

本文轉(zhuǎn)載自微信公眾號(hào)「bugstack蟲(chóng)洞?!?,作者小傅哥  。轉(zhuǎn)載本文請(qǐng)聯(lián)系bugstack蟲(chóng)洞棧公眾號(hào)。

目錄

  • 一、前言
  • 二、面試題
  • 三、什么是循環(huán)依賴?
    • 1. 問(wèn)題描述
    • 2. 問(wèn)題體現(xiàn)
    • 3. 問(wèn)題處理
  • 四、源碼分析
    • 1. 說(shuō)說(shuō)細(xì)節(jié)
    • 2. 處理過(guò)程
    • 3. 依賴解析
  • 五、總結(jié)
  • 六、系列推薦

一、前言

延遲滿足能給你帶來(lái)什么?

大學(xué)有四年時(shí)間,但幾乎所有人都是臨近畢業(yè)才發(fā)現(xiàn)找一份好工作費(fèi)勁,尤其是我能非常熟悉的軟件開(kāi)發(fā)行業(yè),即使是畢業(yè)了還需要額外花錢(qián)到培訓(xùn)機(jī)構(gòu),在學(xué)一遍編程技術(shù)才能出去找工作。好像在校這幾年壓根就沒(méi)學(xué)到什么!

就我個(gè)人而言可能是因?yàn)樯蠈W(xué)期間喜歡編程,也從師哥、師姐那里聽(tīng)到一些關(guān)于畢業(yè)后找工作的不容易,也了解了一些社會(huì)上對(duì)程序員開(kāi)發(fā)技能的要求級(jí)別。也就是得到了這些消息,又加上自己樂(lè)于折騰,我給自己定了一個(gè)每天都能完成的小目標(biāo):

紅塵世界幾個(gè)王,我自不服迎頭上。

日敲代碼兩百行,沖進(jìn)世界五百?gòu)?qiáng)。

哈哈哈,就這么每天兩百行代碼,一個(gè)月就是6千行,一年就是6萬(wàn)行,三年后開(kāi)始實(shí)習(xí)就有18萬(wàn)行,一個(gè)應(yīng)屆實(shí)習(xí)生有將近20萬(wàn)行代碼的敲擊量,幾乎已經(jīng)可以非常熟練的完成各類(lèi)簡(jiǎn)單的工作,在加上實(shí)習(xí)中對(duì)整個(gè)項(xiàng)目流程真正的斷鏈后,找一個(gè)正經(jīng)的開(kāi)發(fā)工作,還是很容易的。

而這時(shí)候找工作的容易,就來(lái)自于你一直以來(lái)的學(xué)習(xí)和沉淀,但如果你沒(méi)經(jīng)過(guò)這些努力,可能等畢業(yè)后就會(huì)變得非?;艁y,最后沒(méi)辦法,只能去一些機(jī)構(gòu)再學(xué)習(xí)一遍。

二、面試題

謝飛機(jī),小記!,以前感覺(jué)Spring沒(méi)啥,看過(guò)一篇getBean,我的天!

謝飛機(jī):面試官,最近我看了 Spring 的 getBean 發(fā)現(xiàn)這里好多東西,還有一個(gè)是要解決循環(huán)依賴的,這玩意面試有啥要問(wèn)的嗎?

面試官:有哇,Spring 是如何解決循環(huán)依賴的?

謝飛機(jī):嗯,通過(guò)三級(jí)緩存提前暴露對(duì)象解決的。

面試官:可以哈,那這三個(gè)緩存里都存放了什么樣的對(duì)象信息呢?

謝飛機(jī):一級(jí)緩存存放的是完整對(duì)象,也叫成品對(duì)象。二級(jí)緩存存放的是半成品對(duì)象,就是那些屬性還沒(méi)賦值的對(duì)象。三級(jí)緩存存放的是 ObjectFactory 類(lèi)型的 lambda 表達(dá)式,就是這用于處理 AOP 循環(huán)依賴的。

面試官:可以呀,謝飛機(jī)有所準(zhǔn)備嘛!那如果沒(méi)有三級(jí)緩存,只有二級(jí)或者一級(jí),能解決循環(huán)依賴嗎?

謝飛機(jī):其實(shí)我看過(guò)資料了,可以解決,只不過(guò) Spring 要保證幾個(gè)事情,只有一級(jí)緩存處理流程沒(méi)法拆分,復(fù)雜度也會(huì)增加,同時(shí)半成品對(duì)象可能會(huì)有空指針異常。而將半成品與成品對(duì)象分開(kāi),處理起來(lái)也更加優(yōu)雅、簡(jiǎn)單、易擴(kuò)展。另外 Spring 的兩大特性中不僅有 IOC 還有 AOP,也就是基于字節(jié)碼增強(qiáng)后的方法,該存放到哪,而三級(jí)緩存最主要,要解決的循環(huán)依賴就是對(duì) AOP 的處理,但如果把 AOP 代理對(duì)象的創(chuàng)建提前,那么二級(jí)緩存也一樣可以解決。但是,這就違背了 Spring 創(chuàng)建對(duì)象的原則,Spring 更喜歡把所有的普通 Bean 都初始化完成,在處理代理對(duì)象的初始化。

面試官:飛機(jī),不錯(cuò)嘛,這次了解了不少。那問(wèn)個(gè)簡(jiǎn)單的,你擼過(guò)循環(huán)依賴的解決方案?

謝飛機(jī):哦哦,這沒(méi)有,沒(méi)實(shí)踐過(guò)!!!確實(shí)應(yīng)該搞一下,試試。

三、什么是循環(huán)依賴?

1. 問(wèn)題描述

了解問(wèn)題的本質(zhì)再分析問(wèn)題,往往更利于對(duì)問(wèn)題有更深入的了解和研究。所以我們?cè)诜治?Spring 關(guān)于循環(huán)依賴的源碼之前,先要了解下什么是循環(huán)依賴。

循環(huán)依賴分為三種,自身依賴于自身、互相循環(huán)依賴、多組循環(huán)依賴。

但無(wú)論循環(huán)依賴的數(shù)量有多少,循環(huán)依賴的本質(zhì)是一樣的。就是你的完整創(chuàng)建依賴于我,而我的完整創(chuàng)建也依賴于你,但我們互相沒(méi)法解耦,最終導(dǎo)致依賴創(chuàng)建失敗。

所以 Spring 提供了除了構(gòu)造函數(shù)注入和原型注入外的,setter循環(huán)依賴注入解決方案。那么我們也可以先來(lái)嘗試下這樣的依賴,如果是我們自己處理的話該怎么解決。

2. 問(wèn)題體現(xiàn)

  1. public class ABTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         new ClazzA(); 
  5.     } 
  6.  
  7.  
  8. class ClazzA { 
  9.  
  10.     private ClazzB b = new ClazzB(); 
  11.  
  12.  
  13. class ClazzB { 
  14.  
  15.     private ClazzA a = new ClazzA(); 
  16.  
  • 這段代碼就是循環(huán)依賴最初的模樣,你中有我,我中有你,運(yùn)行就報(bào)錯(cuò) java.lang.StackOverflowError
  • 這樣的循環(huán)依賴代碼是沒(méi)法解決的,當(dāng)你看到 Spring 中提供了 get/set 或者注解,這樣之所以能解決,首先是進(jìn)行了一定的解耦。讓類(lèi)的創(chuàng)建和屬性的填充分離,先創(chuàng)建出半成品Bean,再處理屬性的填充,完成成品Bean的提供。

3. 問(wèn)題處理

在這部分的代碼中就一個(gè)核心目的,我們來(lái)自己解決一下循環(huán)依賴,方案如下:

  1. public class CircleTest { 
  2.  
  3.     private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 
  4.  
  5.     public static void main(String[] args) throws Exception { 
  6.         System.out.println(getBean(B.class).getA()); 
  7.         System.out.println(getBean(A.class).getB()); 
  8.     } 
  9.  
  10.     private static <T> T getBean(Class<T> beanClass) throws Exception { 
  11.         String beanName = beanClass.getSimpleName().toLowerCase(); 
  12.         if (singletonObjects.containsKey(beanName)) { 
  13.             return (T) singletonObjects.get(beanName); 
  14.         } 
  15.         // 實(shí)例化對(duì)象入緩存 
  16.         Object obj = beanClass.newInstance(); 
  17.         singletonObjects.put(beanName, obj); 
  18.         // 屬性填充補(bǔ)全對(duì)象 
  19.         Field[] fields = obj.getClass().getDeclaredFields(); 
  20.         for (Field field : fields) { 
  21.             field.setAccessible(true); 
  22.             Class<?> fieldClass = field.getType(); 
  23.             String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); 
  24.             field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass)); 
  25.             field.setAccessible(false); 
  26.         } 
  27.         return (T) obj; 
  28.     } 
  29.  
  30.  
  31. class A { 
  32.  
  33.     private B b; 
  34.  
  35.     // ...get/set 
  36.  
  37. class B { 
  38.     private A a; 
  39.  
  40.   // ...get/set 
  • 這段代碼提供了 A、B 兩個(gè)類(lèi),互相有依賴。但在兩個(gè)類(lèi)中的依賴關(guān)系使用的是 setter 的方式進(jìn)行填充。也就是只有這樣才能避免兩個(gè)類(lèi)在創(chuàng)建之初不非得強(qiáng)依賴于另外一個(gè)對(duì)象。
  • getBean,是整個(gè)解決循環(huán)依賴的核心內(nèi)容,A 創(chuàng)建后填充屬性時(shí)依賴 B,那么就去創(chuàng)建 B,在創(chuàng)建 B 開(kāi)始填充時(shí)發(fā)現(xiàn)依賴于 A,但此時(shí) A 這個(gè)半成品對(duì)象已經(jīng)存放在緩存到singletonObjects 中了,所以 B 可以正常創(chuàng)建,在通過(guò)遞歸把 A 也創(chuàng)建完整了。

四、源碼分析

1. 說(shuō)說(shuō)細(xì)節(jié)

通過(guò)上面的例子我們大概了解到,A和B互相依賴時(shí),A創(chuàng)建完后填充屬性B,繼續(xù)創(chuàng)建B,再填充屬性A時(shí)就可以從緩存中獲取了,如下:

那這個(gè)解決循環(huán)依賴的事放到 Spring 中是什么樣呢?展開(kāi)細(xì)節(jié)!

雖然,解決循環(huán)依賴的核心原理一樣,但要放到支撐起整個(gè) Spring 中 IOC、AOP 特性時(shí),就會(huì)變得復(fù)雜一些,整個(gè)處理 Spring 循環(huán)依賴的過(guò)程如下;

  • 以上就是關(guān)于 Spring 中對(duì)于一個(gè)有循環(huán)依賴的對(duì)象獲取過(guò)程,也就是你想要的說(shuō)說(shuō)細(xì)節(jié)
  • 乍一看是挺多流程,但是這些也基本是你在調(diào)試代碼時(shí)候必須經(jīng)過(guò)的代碼片段,拿到這份執(zhí)行流程,再調(diào)試就非常方便了。

2. 處理過(guò)程

關(guān)于本章節(jié)涉及到的案例源碼分析,已更新到 github:https://github.com/fuzhengwei/interview - interview-31

以下是單元測(cè)試中對(duì)AB依賴的獲取Bean操作,重點(diǎn)在于進(jìn)入 getBean 的源碼跟進(jìn);

  1. @Test 
  2. public void test_alias() { 
  3.     BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml"); 
  4.     Bean_A bean_a = beanFactory.getBean("bean_a", Bean_A.class); 
  5.     logger.info("獲取 Bean 通過(guò)別名:{}", bean_a.getBean_b()); 

org.springframework.beans.factory.support.AbstractBeanFactory.java

  1. @Override 
  2. public <T> T getBean(String name, Class<T> requiredType) throws BeansException { 
  3.  return doGetBean(name, requiredType, nullfalse); 
  • 從 getBean 進(jìn)入后,獲取 bean 的操作會(huì)進(jìn)入到 doGetBean。
  • 之所以這樣包裝一層,是因?yàn)?doGetBean 有很多不同入?yún)⒌闹剌d方法,方便外部操作。

doGetBean 方法

  1. protected <T> T doGetBean( 
  2.   final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) 
  3.   throws BeansException { 
  4.   
  5.   // 從緩存中獲取 bean 實(shí)例 
  6.  Object sharedInstance = getSingleton(beanName); 
  7.   
  8.    // mbd.isSingleton() 用于判斷 bean 是否是單例模式 
  9.    if (mbd.isSingleton()) { 
  10.      // 獲取 bean 實(shí)例 
  11.     sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { 
  12.      @Override 
  13.      public Object getObject() throws BeansException { 
  14.       try { 
  15.         // 創(chuàng)建 bean 實(shí)例,createBean 返回的 bean 實(shí)例化好的 
  16.        return createBean(beanName, mbd, args); 
  17.       } 
  18.       catch (BeansException ex) { 
  19.        destroySingleton(beanName); 
  20.        throw ex; 
  21.       } 
  22.      } 
  23.     }); 
  24.     // 后續(xù)的處理操作 
  25.     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 
  26.    } 
  27.     
  28.  // ... 
  29.  
  30.   // 返回 bean 實(shí)例 
  31.  return (T) bean; 
  • 按照在源碼分析的流程圖中可以看到,這一部分是從 getSingleton 先判斷是否有實(shí)例對(duì)象,對(duì)于第一次進(jìn)入是肯定沒(méi)有對(duì)象的,要繼續(xù)往下走。
  • 在判斷 mbd.isSingleton() 單例以后,開(kāi)始使用基于 ObjectFactory 包裝的方式創(chuàng)建 createBean,進(jìn)入后核心邏輯是開(kāi)始執(zhí)行 doCreateBean 操作。

doCreateBean 方法

  1. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) 
  2.   throws BeanCreationException { 
  3.   
  4.    // 創(chuàng)建 bean 實(shí)例,并將 bean 實(shí)例包裝到 BeanWrapper 對(duì)象中返回 
  5.   instanceWrapper = createBeanInstance(beanName, mbd, args); 
  6.   
  7.   // 添加 bean 工廠對(duì)象到 singletonFactories 緩存中 
  8.   addSingletonFactory(beanName, new ObjectFactory<Object>() { 
  9.    @Override 
  10.    public Object getObject() throws BeansException { 
  11.      // 獲取原始對(duì)象的早期引用,在 getEarlyBeanReference 方法中,會(huì)執(zhí)行 AOP 相關(guān)邏輯。若 bean 未被 AOP 攔截,getEarlyBeanReference 原樣返回 bean。 
  12.     return getEarlyBeanReference(beanName, mbd, bean); 
  13.    } 
  14.   }); 
  15.    
  16.  try { 
  17.    // 填充屬性,解析依賴關(guān)系 
  18.   populateBean(beanName, mbd, instanceWrapper); 
  19.   if (exposedObject != null) { 
  20.    exposedObject = initializeBean(beanName, exposedObject, mbd); 
  21.   } 
  22.  } 
  23.   
  24.  // 返回 bean 實(shí)例 
  25.  return exposedObject; 
  • 在 doCreateBean 方法中包括的內(nèi)容較多,但核心主要是創(chuàng)建實(shí)例、加入緩存以及最終進(jìn)行屬性填充,屬性填充就是把一個(gè) bean 的各個(gè)屬性字段涉及到的類(lèi)填充進(jìn)去。
  • createBeanInstance,創(chuàng)建 bean 實(shí)例,并將 bean 實(shí)例包裝到 BeanWrapper 對(duì)象中返回
  • addSingletonFactory,添加 bean 工廠對(duì)象到 singletonFactories 緩存中
  • getEarlyBeanReference,獲取原始對(duì)象的早期引用,在 getEarlyBeanReference 方法中,會(huì)執(zhí)行 AOP 相關(guān)邏輯。若 bean 未被 AOP 攔截,getEarlyBeanReference 原樣返回 bean。
  • populateBean,填充屬性,解析依賴關(guān)系。也就是從這開(kāi)始去找尋 A 實(shí)例中屬性 B,緊接著去創(chuàng)建 B 實(shí)例,最后在返回回來(lái)。

getSingleton 三級(jí)緩存

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) { 
  2.   // 從 singletonObjects 獲取實(shí)例,singletonObjects 是成品 bean 
  3.  Object singletonObject = this.singletonObjects.get(beanName); 
  4.  // 判斷 beanName ,isSingletonCurrentlyInCreation 對(duì)應(yīng)的 bean 是否正在創(chuàng)建中 
  5.  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 
  6.   synchronized (this.singletonObjects) { 
  7.     // 從 earlySingletonObjects 中獲取提前曝光未成品的 bean 
  8.    singletonObject = this.earlySingletonObjects.get(beanName); 
  9.    if (singletonObject == null && allowEarlyReference) { 
  10.      // 獲取相應(yīng)的 bean 工廠 
  11.     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 
  12.     if (singletonFactory != null) { 
  13.       // 提前曝光 bean 實(shí)例,主要用于解決AOP循環(huán)依賴 
  14.      singletonObject = singletonFactory.getObject(); 
  15.       
  16.      // 將 singletonObject 放入緩存中,并將 singletonFactory 從緩存中移除 
  17.      this.earlySingletonObjects.put(beanName, singletonObject); 
  18.      this.singletonFactories.remove(beanName); 
  19.     } 
  20.    } 
  21.   } 
  22.  } 
  23.  return (singletonObject != NULL_OBJECT ? singletonObject : null); 
  • singletonObjects.get(beanName),從 singletonObjects 獲取實(shí)例,singletonObjects 是成品 bean
  • isSingletonCurrentlyInCreation,判斷 beanName ,isSingletonCurrentlyInCreation 對(duì)應(yīng)的 bean 是否正在創(chuàng)建中
  • allowEarlyReference,從 earlySingletonObjects 中獲取提前曝光未成品的 bean
  • singletonFactory.getObject(),提前曝光 bean 實(shí)例,主要用于解決AOP循環(huán)依賴

綜上,是一個(gè)處理循環(huán)依賴的代碼流程,這部分提取出來(lái)的內(nèi)容主要為核心內(nèi)容,并沒(méi)有長(zhǎng)篇大論的全部拆取出來(lái),大家在調(diào)試的時(shí)候會(huì)涉及的比較多,盡可能要自己根據(jù)流程圖操作調(diào)試幾遍。

3. 依賴解析

綜上從我們自己去嘗試解決循環(huán)依賴,學(xué)習(xí)了循環(huán)依賴的核心解決原理。又分析了 Spring 解決的循環(huán)依賴的處理過(guò)程以及核心源碼的分析。那么接下來(lái)我們?cè)诳偨Y(jié)下三級(jí)緩存分別不同的處理過(guò)程,算是一個(gè)總結(jié),也方便大家理解。

1. 一級(jí)緩存能解決嗎?

  • 其實(shí)只有一級(jí)緩存并不是不能解決循環(huán)依賴,就像我們自己做的例子一樣。
  • 但是在 Spring 中如果像我們例子里那么處理,就會(huì)變得非常麻煩,而且也可能會(huì)出現(xiàn) NPE 問(wèn)題。
  • 所以如圖按照 Spring 中代碼處理的流程,我們?nèi)シ治鲆患?jí)緩存這樣存放成品 Bean 的流程中,是不能解決循環(huán)依賴的問(wèn)題的。因?yàn)?A 的成品創(chuàng)建依賴于 B,B的成品創(chuàng)建又依賴于 A,當(dāng)需要補(bǔ)全B的屬性時(shí) A 還是沒(méi)有創(chuàng)建完,所以會(huì)出現(xiàn)死循環(huán)。

2. 二級(jí)緩存能解決嗎?

  • 有了二級(jí)緩存其實(shí)這個(gè)事處理起來(lái)就容易了,一個(gè)緩存用于存放成品對(duì)象,另外一個(gè)緩存用于存放半成品對(duì)象。
  • A 在創(chuàng)建半成品對(duì)象后存放到緩存中,接下來(lái)補(bǔ)充 A 對(duì)象中依賴 B 的屬性。
  • B 繼續(xù)創(chuàng)建,創(chuàng)建的半成品同樣放到緩存中,在補(bǔ)充對(duì)象的 A 屬性時(shí),可以從半成品緩存中獲取,現(xiàn)在 B 就是一個(gè)完整對(duì)象了,而接下來(lái)像是遞歸操作一樣 A 也是一個(gè)完整對(duì)象了。

3. 三級(jí)緩存解決什么?

有了二級(jí)緩存都能解決 Spring 依賴了,怎么要有三級(jí)緩存呢。其實(shí)我們?cè)谇懊娣治鲈创a時(shí)也提到過(guò),三級(jí)緩存主要是解決 Spring AOP 的特性。AOP 本身就是對(duì)方法的增強(qiáng),是 ObjectFactory 類(lèi)型的 lambda 表達(dá)式,而 Spring 的原則又不希望將此類(lèi)類(lèi)型的 Bean 前置創(chuàng)建,所以要存放到三級(jí)緩存中處理。

其實(shí)整體處理過(guò)程類(lèi)似,唯獨(dú)是 B 在填充屬性 A 時(shí),先查詢成品緩存、再查半成品緩存,最后在看看有沒(méi)有單例工程類(lèi)在三級(jí)緩存中。最終獲取到以后調(diào)用 getObject 方法返回代理引用或者原始引用。

至此也就解決了 Spring AOP 所帶來(lái)的三級(jí)緩存問(wèn)題。本章節(jié)涉及到的 AOP 依賴有源碼例子,可以進(jìn)行調(diào)試 https://github.com/fuzhengwei/interview

五、總結(jié)

回顧本文基本以實(shí)際操作的例子開(kāi)始,引導(dǎo)大家對(duì)循環(huán)依賴有一個(gè)整體的認(rèn)識(shí),也對(duì)它的解決方案可以上手的例子,這樣對(duì)后續(xù)的關(guān)于 Spring 對(duì)循環(huán)依賴的解決也就不會(huì)那么陌生了。

通篇全文下來(lái)大家也可以看到,三級(jí)緩存并不是非必須不可,只不過(guò)在滿足 Spring 自身創(chuàng)建的原則下,是必須的。如果你可以下載 Spring 源碼對(duì)這部分代碼進(jìn)行改動(dòng)下,提前創(chuàng)建 AOP 對(duì)象保存到緩存中,那么二級(jí)緩存一樣可以解決循環(huán)依賴問(wèn)題。

 

 

 

關(guān)于循環(huán)依賴可能并不是一個(gè)好的編碼方式,如果在自己的程序中還是要盡可能使用更合理的設(shè)計(jì)模式規(guī)避循環(huán)依賴,可能這些方式會(huì)增加代碼量,但在維護(hù)上會(huì)更加方便。當(dāng)然這不是強(qiáng)制,可以根據(jù)你的需要而來(lái)。

 

責(zé)任編輯:武曉燕 來(lái)源: bugstack蟲(chóng)洞棧
相關(guān)推薦

2024-03-18 00:00:00

SpringBean設(shè)計(jì)

2023-05-04 08:06:27

Spring循環(huán)依賴

2020-12-11 08:04:22

SpringAOPBean

2024-03-04 08:47:17

Spring框架AOP

2020-08-06 00:14:16

Spring IoC依賴注入開(kāi)發(fā)

2025-03-17 00:21:00

2021-09-01 11:45:10

Spring循環(huán)依賴面試

2024-06-05 11:43:10

2023-10-07 08:35:07

依賴注入Spring

2019-11-26 14:30:20

Spring循環(huán)依賴Java

2021-10-21 08:31:31

Spring循環(huán)依賴面試

2024-08-27 11:00:56

單例池緩存bean

2019-09-09 06:30:06

Springboot程序員開(kāi)發(fā)

2020-07-29 10:40:21

Spring循環(huán)依賴Java

2023-10-07 08:40:57

緩存屬性Spring

2021-06-25 09:47:59

Spring循環(huán)依賴Java

2020-02-10 15:50:18

Spring循環(huán)依賴Java

2020-05-07 10:05:58

Spring循環(huán)依賴Java

2011-04-02 15:25:41

Spring

2024-03-14 10:47:12

Spring生命周期阿里
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)