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

Springboot源碼分析之Spring循環(huán)依賴揭秘

新聞 前端
若你是一個有經(jīng)驗的程序員,那你在開發(fā)中必然碰到過這種現(xiàn)象:事務(wù)不生效?;蛟S剛說到這,有的小伙伴就會大驚失色了。

摘要:

若你是一個有經(jīng)驗的程序員,那你在開發(fā)中必然碰到過這種現(xiàn)象:事務(wù)不生效?;蛟S剛說到這,有的小伙伴就會大驚失色了。 Spring 不是解決了循環(huán)依賴問題嗎,它是怎么又會發(fā)生循環(huán)依賴的呢?,接下來就讓我們一起揭秘 Spring 循環(huán)依賴的最本質(zhì)原因。

Spring循環(huán)依賴流程圖

Spring循環(huán)依賴發(fā)生原因

  • 使用了具有代理特性的BeanPostProcessor
  • 典型的有 事務(wù)注解@Transactional,異步注解@Async等 

源碼分析揭秘

  1. protected Object doCreateBean( ... ){ 
  2.         ... 
  3.         boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); 
  4.         if (earlySingletonExposure) { 
  5.             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 
  6.         } 
  7.         ... 
  8.      
  9.         // populateBean這一句特別的關(guān)鍵,它需要給A的屬性賦值,所以此處會去實例化B~~ 
  10.         // 而B我們從上可以看到它就是個普通的Bean(并不需要創(chuàng)建代理對象),實例化完成之后,繼續(xù)給他的屬性A賦值,而此時它會去拿到A的早期引用 
  11.         // 也就在此處在給B的屬性a賦值的時候,會執(zhí)行到上面放進去的Bean A流程中的getEarlyBeanReference()方法  從而拿到A的早期引用~~ 
  12.         // 執(zhí)行A的getEarlyBeanReference()方法的時候,會執(zhí)行自動代理創(chuàng)建器,但是由于A沒有標(biāo)注事務(wù),所以最終不會創(chuàng)建代理,so B合格屬性引用會是A的**原始對象** 
  13.         // 需要注意的是:@Async的代理對象不是在getEarlyBeanReference()中創(chuàng)建的,是在postProcessAfterInitialization創(chuàng)建的代理 
  14.         // 從這我們也可以看出@Async的代理它默認(rèn)并不支持你去循環(huán)引用,因為它并沒有把代理對象的早期引用提供出來~~~(注意這點和自動代理創(chuàng)建器的區(qū)別~) 
  15.      
  16.         // 結(jié)論:此處給A的依賴屬性字段B賦值為了B的實例(因為B不需要創(chuàng)建代理,所以就是原始對象) 
  17.         // 而此處實例B里面依賴的A注入的仍舊為Bean A的普通實例對象(注意  是原始對象非代理對象)  注:此時exposedObject也依舊為原始對象 
  18.         populateBean(beanName, mbd, instanceWrapper); 
  19.          
  20.         // 標(biāo)注有@Async的Bean的代理對象在此處會被生成~~~ 參照類:AsyncAnnotationBeanPostProcessor 
  21.         // 所以此句執(zhí)行完成后  exposedObject就會是個代理對象而非原始對象了 
  22.         exposedObject = initializeBean(beanName, exposedObject, mbd); 
  23.          
  24.         ... 
  25.         // 這里是報錯的重點~~~ 
  26.         if (earlySingletonExposure) { 
  27.             // 上面說了A被B循環(huán)依賴進去了,所以此時A是被放進了二級緩存的,所以此處earlySingletonReference 是A的原始對象的引用 
  28.             // (這也就解釋了為何我說:如果A沒有被循環(huán)依賴,是不會報錯不會有問題的   因為若沒有循環(huán)依賴earlySingletonReference =null后面就直接return了) 
  29.             Object earlySingletonReference = getSingleton(beanName, false); 
  30.             if (earlySingletonReference != null) { 
  31.                 // 上面分析了exposedObject 是被@Aysnc代理過的對象, 而bean是原始對象 所以此處不相等  走else邏輯 
  32.                 if (exposedObject == bean) { 
  33.                     exposedObject = earlySingletonReference; 
  34.                 } 
  35.                 // allowRawInjectionDespiteWrapping 標(biāo)注是否允許此Bean的原始類型被注入到其它Bean里面,即使自己最終會被包裝(代理) 
  36.                 // 默認(rèn)是false表示不允許,如果改為true表示允許,就不會報錯啦。這是我們后面講的決方案的其中一個方案~~~ 
  37.                 // 另外dependentBeanMap記錄著每個Bean它所依賴的Bean的Map~~~~ 
  38.                 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 
  39.                     // 我們的Bean A依賴于B,so此處值為["b"] 
  40.                     String[] dependentBeans = getDependentBeans(beanName); 
  41.                     Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); 
  42.      
  43.                     // 對所有的依賴進行一一檢查~    比如此處B就會有問題 
  44.                     // “b”它經(jīng)過removeSingletonIfCreatedForTypeCheckOnly最終返返回false  因為alreadyCreated里面已經(jīng)有它了表示B已經(jīng)完全創(chuàng)建完成了~~~ 
  45.                     // 而b都完成了,所以屬性a也賦值完成兒聊 但是B里面引用的a和主流程我這個A竟然不相等,那肯定就有問題(說明不是最終的)~~~ 
  46.                     // so最終會被加入到actualDependentBeans里面去,表示A真正的依賴~~~ 
  47.                     for (String dependentBean : dependentBeans) { 
  48.                         if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 
  49.                             actualDependentBeans.add(dependentBean); 
  50.                         } 
  51.                     } 
  52.          
  53.                     // 若存在這種真正的依賴,那就報錯了~~~  則個異常就是上面看到的異常信息 
  54.                     if (!actualDependentBeans.isEmpty()) { 
  55.                         throw new BeanCurrentlyInCreationException(beanName, 
  56.                                 "Bean with name '" + beanName + "' has been injected into other beans [" + 
  57.                                 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + 
  58.                                 "] in its raw version as part of a circular reference, but has eventually been " + 
  59.                                 "wrapped. This means that said other beans do not use the final version of the " + 
  60.                                 "bean. This is often the result of over-eager type matching - consider using " + 
  61.                                 "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); 
  62.                     } 
  63.                 } 
  64.             } 
  65.         } 
  66.         ... 
  67.     } 

問題簡化

  • 發(fā)生循環(huán)依賴時候 Object earlySingletonReference = getSingleton(beanName, false); 肯定有值
  • 緩存工廠 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 將給實例對象添加 SmartInstantiationAwareBeanPostProcessor
  • AbstractAutoProxyCreator 是 SmartInstantiationAwareBeanPostProcessor 的子類,一定記住了,一定記住, SmartInstantiationAwareBeanPostProcessor 的子類很關(guān)鍵?。。。。?/li>
  • exposedObject = initializeBean(beanName, exposedObject, mbd); 進行 BeanPostProcessor后置處理,注意是 BeanPostProcessor ?。。。。?/li>

Spring 的循環(huán)依賴被它的三級緩存給輕易解決了,但是這2個地方的后置處理帶來了 循環(huán)依賴的問題。

對比AbstractAdvisorAutoProxyCreator和AsyncAnnotationBeanPostProcessor

由于 SmartInstantiationAwareBeanPostProcessor 的子類會在兩處都會執(zhí)行后置處理,所以前后都會相同的對象引用,不會發(fā)生循環(huán)依賴問題,異步注解就不行了 ,至于為什么?自己看上面的分析,仔細(xì)看哦!

如何解決循環(huán)依賴?

  • 改變加載順序
  • @Lazy 注解
  • allowRawInjectionDespiteWrapping 設(shè)置為 true (利用了判斷的那條語句)
  • 別使用相關(guān)的 BeanPostProcessor 設(shè)計到的注解,,哈哈 這不太現(xiàn)實。 
     

@Lazy

@Lazy 一般含義是懶加載,它只會作用于 BeanDefinition.setLazyInit() 。而此處給它增加了一個能力:延遲處理(代理處理)

  1. // @since 4.0 出現(xiàn)得挺晚,它支持到了@Lazy  是功能最全的AutowireCandidateResolver 
  2.     public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver { 
  3.         // 這是此類本身唯一做的事,此處精析  
  4.         // 返回該 lazy proxy 表示延遲初始化,實現(xiàn)過程是查看在 @Autowired 注解處是否使用了 @Lazy = true 注解  
  5.         @Override 
  6.         @Nullable 
  7.         public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { 
  8.             // 如果isLazy=true  那就返回一個代理,否則返回null 
  9.             // 相當(dāng)于若標(biāo)注了@Lazy注解,就會返回一個代理(當(dāng)然@Lazy注解的value值不能是false) 
  10.             return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); 
  11.         } 
  12.      
  13.         // 這個比較簡單,@Lazy注解標(biāo)注了就行(value屬性默認(rèn)值是true) 
  14.         // @Lazy支持標(biāo)注在屬性上和方法入?yún)⑸蟸~~  這里都會解析 
  15.         protected boolean isLazy(DependencyDescriptor descriptor) { 
  16.             for (Annotation ann : descriptor.getAnnotations()) { 
  17.                 Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); 
  18.                 if (lazy != null && lazy.value()) { 
  19.                     return true
  20.                 } 
  21.             } 
  22.             MethodParameter methodParam = descriptor.getMethodParameter(); 
  23.             if (methodParam != null) { 
  24.                 Method method = methodParam.getMethod(); 
  25.                 if (method == null || void.class == method.getReturnType()) { 
  26.                     Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class); 
  27.                     if (lazy != null && lazy.value()) { 
  28.                         return true
  29.                     } 
  30.                 } 
  31.             } 
  32.             return false
  33.         } 
  34.      
  35.         // 核心內(nèi)容,是本類的靈魂~~~ 
  36.         protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) { 
  37.             Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory, 
  38.                     "BeanFactory needs to be a DefaultListableBeanFactory"); 
  39.      
  40.             // 這里毫不客氣的使用了面向?qū)崿F(xiàn)類編程,使用了DefaultListableBeanFactory.doResolveDependency()方法~~~ 
  41.             final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory(); 
  42.      
  43.             //TargetSource 是它實現(xiàn)懶加載的核心原因,在AOP那一章節(jié)了重點提到過這個接口,此處不再敘述 
  44.             // 它有很多的著名實現(xiàn)如HotSwappableTargetSource、SingletonTargetSource、LazyInitTargetSource、 
  45.             //SimpleBeanTargetSource、ThreadLocalTargetSource、PrototypeTargetSource等等非常多 
  46.             // 此處因為只需要自己用,所以采用匿名內(nèi)部類的方式實現(xiàn)~~~ 此處最重要是看getTarget方法,它在被使用的時候(也就是代理對象真正使用的時候執(zhí)行~~~) 
  47.             TargetSource ts = new TargetSource() { 
  48.                 @Override 
  49.                 public Class<?> getTargetClass() { 
  50.                     return descriptor.getDependencyType(); 
  51.                 } 
  52.                 @Override 
  53.                 public boolean isStatic() { 
  54.                     return false
  55.                 } 
  56.          
  57.                 // getTarget是調(diào)用代理方法的時候會調(diào)用的,所以執(zhí)行每個代理方法都會執(zhí)行此方法,這也是為何doResolveDependency 
  58.                 // 我個人認(rèn)為它在效率上,是存在一定的問題的~~~所以此處建議盡量少用@Lazy~~~    
  59.                 //不過效率上應(yīng)該還好,對比http、序列化反序列化處理,簡直不值一提  所以還是無所謂  用吧 
  60.                 @Override 
  61.                 public Object getTarget() { 
  62.                     Object target = beanFactory.doResolveDependency(descriptor, beanName, nullnull); 
  63.                     if (target == null) { 
  64.                         Class<?> type = getTargetClass(); 
  65.                         // 對多值注入的空值的友好處理(不要用null) 
  66.                         if (Map.class == type) { 
  67.                             return Collections.emptyMap(); 
  68.                         } else if (List.class == type) { 
  69.                             return Collections.emptyList(); 
  70.                         } else if (Set.class == type || Collection.class == type) { 
  71.                             return Collections.emptySet(); 
  72.                         } 
  73.                         throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(), 
  74.                                 "Optional dependency not present for lazy injection point"); 
  75.                     } 
  76.                     return target; 
  77.                 } 
  78.                 @Override 
  79.                 public void releaseTarget(Object target) { 
  80.                 } 
  81.             };    
  82.      
  83.             // 使用ProxyFactory  給ts生成一個代理 
  84.             // 由此可見最終生成的代理對象的目標(biāo)對象其實是TargetSource,而TargetSource的目標(biāo)才是我們業(yè)務(wù)的對象 
  85.             ProxyFactory pf = new ProxyFactory(); 
  86.             pf.setTargetSource(ts); 
  87.             Class<?> dependencyType = descriptor.getDependencyType(); 
  88.              
  89.             // 如果注入的語句是這么寫的private AInterface a;  那這類就是借口 值是true 
  90.             // 把這個接口類型也得放進去(不然這個代理都不屬于這個類型,反射set的時候豈不直接報錯了嗎????) 
  91.             if (dependencyType.isInterface()) { 
  92.                 pf.addInterface(dependencyType); 
  93.             } 
  94.             return pf.getProxy(beanFactory.getBeanClassLoader()); 
  95.         } 
  96.     } 

標(biāo)注有 @Lazy 注解完成注入的時候,最終注入只是一個此處臨時生成的代理對象,只有在真正執(zhí)行目標(biāo)方法的時候才會去容器內(nèi)拿到真是的 bean 實例來執(zhí)行目標(biāo)方法。

利用allowRawInjectionDespiteWrapping屬性來強制改變判斷

  1. @Component 
  2.     public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 
  3.         @Override 
  4.         public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  5.             ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true); 
  6.         } 
  7.     } 

這樣會導(dǎo)致容器里面的是代理對象,暴露給其他實例的是原始引用,導(dǎo)致不生效了。由于它只對循環(huán)依賴內(nèi)的 Bean 受影響,所以影響范圍并不是全局,因此當(dāng)找不到更好辦法的時候,此種這樣也不失是一個不錯的方案。

責(zé)任編輯:張燕妮 來源: 博客園精華區(qū)
相關(guān)推薦

2022-08-17 07:52:31

Spring循環(huán)依賴單例池

2023-05-04 08:06:27

Spring循環(huán)依賴

2025-03-17 00:21:00

2022-05-12 18:30:12

SpringBoot循環(huán)依賴初始化

2021-09-01 11:45:10

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

2024-06-05 11:43:10

2021-05-06 07:58:57

Spring BeanIOCAOP

2022-01-26 10:29:24

微服務(wù)循環(huán)依賴代碼

2020-05-18 08:11:57

Spring循環(huán)依賴

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

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

2020-10-21 09:11:52

Spring Boot源碼分析代碼

2024-09-09 09:29:05

2011-05-26 10:05:48

MongoDB
點贊
收藏

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