成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring 處理循環(huán)依賴只使用二級緩存,可以嗎?

存儲 存儲軟件
先說一下什么是循環(huán)依賴,Spring在初始化A的時候需要注入B,而初始化B的時候需要注入A,在Spring啟動后這2個Bean都要被初始化完成。

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

先說一下什么是循環(huán)依賴,Spring在初始化A的時候需要注入B,而初始化B的時候需要注入A,在Spring啟動后這2個Bean都要被初始化完成。

Spring的循環(huán)依賴有4種場景:

  • 構(gòu)造器的循環(huán)依賴(singleton,prototype)
  • 屬性的循環(huán)依賴(singleton,prototype)

「spring目前只支持singleton類型的屬性循環(huán)依賴」

構(gòu)造器的循環(huán)依賴

  1. @Component 
  2. public class ConstructorA { 
  3.  
  4.  private ConstructorB constructorB; 
  5.  
  6.  @Autowired 
  7.  public ConstructorA(ConstructorB constructorB) { 
  8.   this.constructorB = constructorB; 
  9.  } 
  1. @Component 
  2. public class ConstructorB { 
  3.  
  4.  private ConstructorA constructorA; 
  5.  
  6.  @Autowired 
  7.  public ConstructorB(ConstructorA constructorA) { 
  8.   this.constructorA = constructorA; 
  9.  } 
  1. @Configuration 
  2. @ComponentScan("com.javashitang.dependency.constructor"
  3. public class ConstructorConfig { 
  1. public class ConstructorMain { 
  2.  
  3.  public static void main(String[] args) { 
  4.   AnnotationConfigApplicationContext context = 
  5.     new AnnotationConfigApplicationContext(ConstructorConfig.class); 
  6.   System.out.println(context.getBean(ConstructorA.class)); 
  7.   System.out.println(context.getBean(ConstructorB.class)); 
  8.  } 

運行ConstructorMain的main方法的時候會在第一行就報異常,說明Spring沒辦法初始化所有的Bean,即上面這種形式的循環(huán)依賴Spring無法解決。

「構(gòu)造器的循環(huán)依賴,可以在構(gòu)造函數(shù)中使用@Lazy注解延遲加載。在注入依賴時,先注入代理對象,當(dāng)首次使用時再創(chuàng)建對象完成注入」

  1. @Autowired 
  2. public ConstructorB(@Lazy ConstructorA constructorA) { 
  3.  this.constructorA = constructorA; 

因為我們主要關(guān)注屬性的循環(huán)依賴,構(gòu)造器的循環(huán)依賴就不做過多分析了。

屬性的循環(huán)依賴

先演示一下什么是屬性的循環(huán)依賴。

  1. @Data 
  2. @Component 
  3. public class A { 
  4.  
  5.     @Autowired 
  6.     private B b; 
  1. @Data 
  2. @Component 
  3. public class B { 
  4.  
  5.     @Autowired 
  6.     private A a; 
  1. @Configuration 
  2. @EnableAspectJAutoProxy 
  3. @ComponentScan("com.javashitang.dependency"
  4. public class Config { 
  1. public class Main { 
  2.  
  3.     public static void main(String[] args) { 
  4.         AnnotationConfigApplicationContext context = 
  5.                 new AnnotationConfigApplicationContext(Config.class); 
  6.         System.out.println(context.getBean(A.class).getB() == context.getBean(B.class)); 
  7.         System.out.println(context.getBean(B.class).getA() == context.getBean(A.class)); 
  8.     } 

Spring容器正常啟動,運行結(jié)果為true,想實現(xiàn)類似的功能并不難,我寫個demo演示一下。

  1. public class DependencyDemoV1 { 
  2.  
  3.     private static final Map<String, Object> singletonObjects = 
  4.             new HashMap<>(256); 
  5.  
  6.     @SneakyThrows 
  7.     public static <T> T getBean(Class<T> beanClass) { 
  8.         String beanName = beanClass.getSimpleName(); 
  9.         if (singletonObjects.containsKey(beanName)) { 
  10.             return (T) singletonObjects.get(beanName); 
  11.         } 
  12.         // 實例化bean 
  13.         Object object = beanClass.getDeclaredConstructor().newInstance(); 
  14.         singletonObjects.put(beanName, object); 
  15.         // 開始初始化bean,即填充屬性 
  16.         Field[] fields = object.getClass().getDeclaredFields(); 
  17.         for (Field field : fields) { 
  18.             field.setAccessible(true); 
  19.             // 獲取需要注入字段的class 
  20.             Class<?> fieldClass = field.getType(); 
  21.             field.set(object, getBean(fieldClass)); 
  22.         } 
  23.         return (T) object; 
  24.     } 
  25.  
  26.     public static void main(String[] args) { 
  27.         // 假裝掃描出來的類 
  28.         Class[] classes = {A.class, B.class}; 
  29.         for (Class aClass : classes) { 
  30.             getBean(aClass); 
  31.         } 
  32.         System.out.println(getBean(A.class).getB() == getBean(B.class)); 
  33.         System.out.println(getBean(B.class).getA() == getBean(A.class)); 
  34.     } 
  35.  

「在開始后面的內(nèi)容的時候,我們先明確2個概念」

實例化:調(diào)用構(gòu)造函數(shù)將對象創(chuàng)建出來 初始化:調(diào)用構(gòu)造函數(shù)將對象創(chuàng)建出來后,給對象的屬性也被賦值。

可以看到只用了一個map就實現(xiàn)了循環(huán)依賴的實現(xiàn),但這種實現(xiàn)有個小缺陷,singletonObjects中的類有可能只是完成了實例化,并沒有完成初始化。

而在spring中singletonObjects中的類都完成了初始化,因為我們?nèi)卫鼴ean的時候都是從singletonObjects中取的,不可能讓我們獲取到?jīng)]有初始化完成的對象。

所以我們來寫第二個實現(xiàn),「用singletonObjects存初始化完成的對象,而用earlySingletonObjects暫存實例化完成的對象,等對象初始化完畢再將對象放入singletonObjects,并從earlySingletonObjects刪除」。

  1. public class DependencyDemoV2 { 
  2.  
  3.     private static final Map<String, Object> singletonObjects = 
  4.             new HashMap<>(256); 
  5.  
  6.     private static final Map<String, Object> earlySingletonObjects = 
  7.             new HashMap<>(256); 
  8.  
  9.     @SneakyThrows 
  10.     public static <T> T getBean(Class<T> beanClass) { 
  11.         String beanName = beanClass.getSimpleName(); 
  12.         if (singletonObjects.containsKey(beanName)) { 
  13.             return (T) singletonObjects.get(beanName); 
  14.         } 
  15.         if (earlySingletonObjects.containsKey(beanName)) { 
  16.             return (T) earlySingletonObjects.get(beanName); 
  17.         } 
  18.         // 實例化bean 
  19.         Object object = beanClass.getDeclaredConstructor().newInstance(); 
  20.         earlySingletonObjects.put(beanName, object); 
  21.         // 開始初始化bean,即填充屬性 
  22.         Field[] fields = object.getClass().getDeclaredFields(); 
  23.         for (Field field : fields) { 
  24.             field.setAccessible(true); 
  25.             // 獲取需要注入字段的class 
  26.             Class<?> fieldClass = field.getType(); 
  27.             field.set(object, getBean(fieldClass)); 
  28.         } 
  29.         singletonObjects.put(beanName, object); 
  30.         earlySingletonObjects.remove(beanName); 
  31.         return (T) object; 
  32.     } 
  33.  
  34.     public static void main(String[] args) { 
  35.         // 假裝掃描出來的類 
  36.         Class[] classes = {A.class, B.class}; 
  37.         for (Class aClass : classes) { 
  38.             getBean(aClass); 
  39.         } 
  40.         System.out.println(getBean(A.class).getB() == getBean(B.class)); 
  41.         System.out.println(getBean(B.class).getA() == getBean(A.class)); 
  42.     } 
  43.  

現(xiàn)在的實現(xiàn)和spring保持一致了,并且只用了2級緩存。spring為什么搞第三個緩存呢?「第三個緩存主要和代理對象相關(guān)」

我還是把上面的例子改進(jìn)一下,改成用3級緩存的實現(xiàn):

  1. public interface ObjectFactory<T> { 
  2.     T getObject(); 
  1. public class DependencyDemoV3 { 
  2.  
  3.     private static final Map<String, Object> singletonObjects = 
  4.             new HashMap<>(256); 
  5.  
  6.     private static final Map<String, Object> earlySingletonObjects = 
  7.             new HashMap<>(256); 
  8.  
  9.     private static final Map<String, ObjectFactory<?>> singletonFactories = 
  10.             new HashMap<>(256); 
  11.  
  12.     @SneakyThrows 
  13.     public static <T> T getBean(Class<T> beanClass) { 
  14.         String beanName = beanClass.getSimpleName(); 
  15.         if (singletonObjects.containsKey(beanName)) { 
  16.             return (T) singletonObjects.get(beanName); 
  17.         } 
  18.         if (earlySingletonObjects.containsKey(beanName)) { 
  19.             return (T) earlySingletonObjects.get(beanName); 
  20.         } 
  21.         ObjectFactory<?> singletonFactory = singletonFactories.get(beanName); 
  22.         if (singletonFactory != null) { 
  23.             return (T) singletonFactory.getObject(); 
  24.         } 
  25.         // 實例化bean 
  26.         Object object = beanClass.getDeclaredConstructor().newInstance(); 
  27.         singletonFactories.put(beanName, () -> { 
  28.             Object proxy = createProxy(object); 
  29.             singletonFactories.remove(beanName); 
  30.             earlySingletonObjects.put(beanName, proxy); 
  31.             return proxy; 
  32.         }); 
  33.         // 開始初始化bean,即填充屬性 
  34.         Field[] fields = object.getClass().getDeclaredFields(); 
  35.         for (Field field : fields) { 
  36.             field.setAccessible(true); 
  37.             // 獲取需要注入字段的class 
  38.             Class<?> fieldClass = field.getType(); 
  39.             field.set(object, getBean(fieldClass)); 
  40.         } 
  41.         createProxy(object); 
  42.         singletonObjects.put(beanName, object); 
  43.         singletonFactories.remove(beanName); 
  44.         earlySingletonObjects.remove(beanName); 
  45.         return (T) object; 
  46.     } 
  47.  
  48.     public static Object createProxy(Object object) { 
  49.         // 因為這個方法有可能被執(zhí)行2次,所以這里應(yīng)該有個判斷 
  50.         // 如果之前提前進(jìn)行過aop操作則直接返回,知道意思就行,不寫了哈 
  51.         // 需要aop的話則返回代理對象,否則返回傳入的對象 
  52.         return object; 
  53.     } 
  54.  
  55.     public static void main(String[] args) { 
  56.         // 假裝掃描出來的類 
  57.         Class[] classes = {A.class, B.class}; 
  58.         for (Class aClass : classes) { 
  59.             getBean(aClass); 
  60.         } 
  61.         System.out.println(getBean(A.class).getB() == getBean(B.class)); 
  62.         System.out.println(getBean(B.class).getA() == getBean(A.class)); 
  63.     } 
  64.  

「為什么要包裝一個ObjectFactory對象?」

如果創(chuàng)建的Bean有對應(yīng)的aop代理,那其他對象注入時,注入的應(yīng)該是對應(yīng)的代理對象;「但是Spring無法提前知道這個對象是不是有循環(huán)依賴的情況」,而正常情況下(沒有循環(huán)依賴情況),Spring都是在對象初始化后才創(chuàng)建對應(yīng)的代理。這時候Spring有兩個選擇:

  • 不管有沒有循環(huán)依賴,實例化后就直接創(chuàng)建好代理對象,并將代理對象放入緩存,出現(xiàn)循環(huán)依賴時,其他對象直接就可以取到代理對象并注入(只需要2級緩存,singletonObjects和earlySingletonObjects即可)
  • 「不提前創(chuàng)建好代理對象,在出現(xiàn)循環(huán)依賴被其他對象注入時,才提前生成代理對象(此時只完成了實例化)。這樣在沒有循環(huán)依賴的情況下,Bean還是在初始化完成才生成代理對象」(需要3級緩存)
  • 「所以到現(xiàn)在為止你知道3級緩存的作用了把,主要是為了正常情況下,代理對象能在初始化完成后生成,而不用提前生成」
緩存 說明
singletonObjects 第一級緩存,存放初始化完成的Bean
earlySingletonObjects 第二級緩存,存放實例化完成的Bean,有可能被進(jìn)行了代理
singletonFactories 延遲生成代理對象

源碼解析

獲取Bean的時候先嘗試從3級緩存中獲取,和我們上面的Demo差不多哈!

DefaultSingletonBeanRegistry#getSingleton

當(dāng)從緩存中獲取不到時,會進(jìn)行創(chuàng)建 AbstractAutowireCapableBeanFactory#doCreateBean(刪除了部分代碼哈)

發(fā)生循環(huán)依賴時,會從工廠里獲取代理對象哈!

當(dāng)開啟aop代理時,SmartInstantiationAwareBeanPostProcessor的一個實現(xiàn)類有AbstractAutoProxyCreator

AbstractAutoProxyCreator#getEarlyBeanReference

getEarlyBeanReference方法提前進(jìn)行代理,為了防止后面再次進(jìn)行代理,需要用earlyProxyReferences記錄一下,這個Bean已經(jīng)被代理過了,不用再代理了。

AbstractAutoProxyCreator#postProcessAfterInitialization

這個方法是進(jìn)行aop代理的地方,因為有可能提前代理了,所以先根據(jù)earlyProxyReferences判斷一下,是否提前代理了,提前代理過就不用代理了。

當(dāng)bean初始化完畢,會放入一級緩存,并從二三級緩存刪除。

DefaultSingletonBeanRegistry#addSingleton

發(fā)生循環(huán)依賴時,整體的執(zhí)行流程如下:

本文轉(zhuǎn)載自微信公眾號「Java識堂」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java識堂公眾號。

 

責(zé)任編輯:武曉燕 來源: Java識堂
相關(guān)推薦

2022-12-02 12:01:30

Spring緩存生命周期

2022-03-01 18:03:06

Spring緩存循環(huán)依賴

2025-06-26 01:55:00

2009-06-18 15:24:35

Hibernate二級

2013-09-08 23:30:56

EF Code Fir架構(gòu)設(shè)計MVC架構(gòu)設(shè)計

2009-09-21 14:59:31

Hibernate二級

2009-09-24 11:04:56

Hibernate二級

2025-04-29 07:06:20

2009-06-10 15:00:58

Hibernate二級配置

2009-09-21 13:31:10

Hibernate 3

2009-09-21 14:39:40

Hibernate二級

2009-09-23 09:37:07

Hibernate緩存

2009-08-13 18:12:12

Hibernate 3

2024-12-03 14:38:07

CaffeineRedis二級緩存

2023-12-12 17:44:13

三級緩存Bean

2019-08-21 14:34:41

2023-02-26 11:15:42

緩存循環(huán)依賴

2015-06-11 10:12:26

Android圖片加載緩存

2023-04-27 08:18:10

MyBatis緩存存儲

2024-03-18 00:00:00

SpringBean設(shè)計
點贊
收藏

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

主站蜘蛛池模板: 有码一区 | 伊人网站在线 | 在线免费观看黄色av | 久久y| 亚洲精品一区二区三区蜜桃久 | 久久精品国产亚洲 | 国产精品久久久久久久久图文区 | 欧美久久一区二区三区 | 欧美精品网站 | 情侣酒店偷拍一区二区在线播放 | 日韩二三区 | 99精品国自产在线 | 91在线精品播放 | 99精品电影 | 国内精品久久精品 | 911网站大全在线观看 | 青青久在线视频 | 久久久久久久久久久福利观看 | 亚洲乱码一区二区三区在线观看 | 一区日韩 | 中文字幕乱码一区二区三区 | 日韩一区在线观看视频 | 天天干狠狠操 | 国产成人免费在线观看 | 久久九精品 | 欧美精品网站 | 成人精品国产免费网站 | 国产精品精品久久久 | 国产午夜视频 | 91爱爱·com| 国产成人综合亚洲欧美94在线 | 91在线免费观看网站 | 亚洲精品91 | 国产一区二区免费 | 少妇午夜一级艳片欧美精品 | 国产精品一区在线 | 久久久久久黄 | 国产一级特黄视频 | 日韩毛片在线视频 | www.久草.com | 国产人成精品一区二区三 |