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

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

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

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

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

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

  • 構(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.  } 

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

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

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

因?yàn)槲覀冎饕P(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容器正常啟動(dòng),運(yùn)行結(jié)果為true,想實(shí)現(xiàn)類似的功能并不難,我寫個(gè)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.         // 實(shí)例化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)容的時(shí)候,我們先明確2個(gè)概念」

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

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

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

所以我們來寫第二個(gè)實(shí)現(xiàn),「用singletonObjects存初始化完成的對(duì)象,而用earlySingletonObjects暫存實(shí)例化完成的對(duì)象,等對(duì)象初始化完畢再將對(duì)象放入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.         // 實(shí)例化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)在的實(shí)現(xiàn)和spring保持一致了,并且只用了2級(jí)緩存。spring為什么搞第三個(gè)緩存呢?「第三個(gè)緩存主要和代理對(duì)象相關(guān)」

我還是把上面的例子改進(jìn)一下,改成用3級(jí)緩存的實(shí)現(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.         // 實(shí)例化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.         // 因?yàn)檫@個(gè)方法有可能被執(zhí)行2次,所以這里應(yīng)該有個(gè)判斷 
  50.         // 如果之前提前進(jìn)行過aop操作則直接返回,知道意思就行,不寫了哈 
  51.         // 需要aop的話則返回代理對(duì)象,否則返回傳入的對(duì)象 
  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.  

「為什么要包裝一個(gè)ObjectFactory對(duì)象?」

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

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

源碼解析

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

DefaultSingletonBeanRegistry#getSingleton

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

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

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

AbstractAutoProxyCreator#getEarlyBeanReference

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

AbstractAutoProxyCreator#postProcessAfterInitialization

這個(gè)方法是進(jìn)行aop代理的地方,因?yàn)橛锌赡芴崆按砹?,所以先根?jù)earlyProxyReferences判斷一下,是否提前代理了,提前代理過就不用代理了。

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

DefaultSingletonBeanRegistry#addSingleton

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

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

 

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

2022-03-01 18:03:06

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

2022-12-02 12:01:30

Spring緩存生命周期

2009-06-18 15:24:35

Hibernate二級(jí)

2013-09-08 23:30:56

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

2009-09-21 14:59:31

Hibernate二級(jí)

2009-09-24 11:04:56

Hibernate二級(jí)

2009-06-10 15:00:58

Hibernate二級(jí)配置

2009-09-21 14:39:40

Hibernate二級(jí)

2009-09-21 13:31:10

Hibernate 3

2009-09-23 09:37:07

Hibernate緩存

2009-08-13 18:12:12

Hibernate 3

2024-12-03 14:38:07

CaffeineRedis二級(jí)緩存

2023-12-12 17:44:13

三級(jí)緩存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緩存存儲(chǔ)

2024-03-18 00:00:00

SpringBean設(shè)計(jì)

2022-05-08 19:23:28

Spring循環(huán)依賴

2023-09-26 07:49:11

AOP代理spring
點(diǎn)贊
收藏

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