免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

      Spirng 循環(huán)依賴報錯:Is there an unresolvable circular reference?

      Spirng 循環(huán)依賴報錯:Is there an unresolvable circular reference?

      1:前言

      最近在項目中遇到了一次循環(huán)依賴報錯的問題,雖然解決的很快,但是有些不明白的地方,特此記錄。在此我把 bean 的結構和 注入方式單獨拎出來進行演示

      1.1:報錯提示

      1.2:錯誤日志

      Exception encountered during context initialization – cancelling refresh attempt:org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘brokenComponent’: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tubunComponent’ defined in class path resource [com/dev/config/sprngcache3/TwConfig.class]: Unsatisfied dependency expressed through method ‘tubunComponent’ parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘denpaComponent’: Unsatisfied dependency expressed through field ‘tolenComponent’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘tolenComponent’: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘tubunComponent’:Requested bean is currently in creation:Is there an unresolvable circular reference?

      1.3:stack overflow問題描述

      Requested bean is currently in creation: Is there an unresolvable circular reference?

      點擊進入 stack overflow 問題描述

      1.4:bean 依賴結構

      BrokenComponent:

      @Componentpublic class BrokenComponent { @Autowired private TolenComponent tolenComponent; @Resource private TubunComponent tubunComponent;}

      TolenComponent:

      @Transactional@Componentpublic class TolenComponent { @Resource private TubunComponent tubunComponent;}

      TubunComponent:

      public class TubunComponent { private DenpaComponent denpaComponent; public TubunComponent(DenpaComponent denpaComponent) { this.denpaComponent = denpaComponent; }}

      TwConfig:

      @Configurationpublic class TwConfig { @Bean public TubunComponent tubunComponent(DenpaComponent denpaComponent) { return new TubunComponent(denpaComponent); }}

      DenpaComponent:

      @Componentpublic class DenpaComponent { @Autowired private TolenComponent tolenComponent;}

      除了 tubunComponent 是構造器注入,其他的bean 都是set 注入。分析bean依賴圖可以發(fā)現(xiàn)確實是循環(huán)依賴的問題。

      2:問題分析

      由于本人對spring bean 的加載機制不是很清晰,這次特意花了幾天時間做了梳理。

      2.1:spring 的 bean 加載過程

      2.2:spring 的三級緩存

      解決了什么問題?

      推薦博客:https://cloud.tencent.com/developer/article/1497692

      2.3:Spring 容器加載過程中比較重要的類/屬性

      類名稱

      方法

      作用

      DefaultListableBeanFactory

      preInstantiateSingletons

      Trigger initialization of all non-lazy singleton beans…/ 觸發(fā)所有非懶加載的單例bean進行初始化

      AbstractBeanFactory

      getBean/doGetBean

      從容器中獲取bean

      DefaultSingletonBeanRegistry

      getSingleton

      從緩存中獲取單例實例,沒有則走單例的創(chuàng)建流程

      DefaultSingletonBeanRegistry

      beforeSingletonCreation

      創(chuàng)建單例之前會將單例放在set集合(singletonsCurrentlyInCreation)中,有檢查bean是否被循環(huán)依賴的作用

      DefaultSingletonBeanRegistry

      beforeSingletonCreation

      創(chuàng)建單例之后會將單例從set集合(singletonsCurrentlyInCreation)中移除

      AbstractAutowireCapableBeanFactory

      createBean/doCreateBean

      創(chuàng)建單例bean

      AbstractAutowireCapableBeanFactory

      populateBean

      對bean進行屬性賦值(往往是二級緩存中的bean)

      AbstractAutowireCapableBeanFactory

      initializeBean

      對屬性賦值之后的bean進行初始化,加載RootBeanDefinition信息,這一步做完才是一個完整的可用的bean

      public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {/** Cache of singleton objects: bean name to bean instance.(一級緩存,存放已初始化完畢的bean) */private final Map singletonObjects = new ConcurrentHashMap(256);/** Cache of singleton factories: bean name to ObjectFactory.(三級緩存,存放未進行過初始化的beanFactory) */private final Map singletonFactories = new HashMap(16);/** Cache of early singleton objects: bean name to bean instance. (二級緩存,存放已實例化,但未進行裝配過屬性的bean)*/private final Map earlySingletonObjects = new HashMap(16);/** Set of registered singletons, containing the bean names in registration order.(存放已注冊了的單例集合 = singletonObjects.size + earlySingletonObjects)*/private final Set registeredSingletons = new LinkedHashSet(256);/** Names of beans that are currently in creation.(存放正在被創(chuàng)建的實例,用于檢查是否有循環(huán)依賴) */private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16)); ….}

      2.4:注冊后處理器->BeanPostProcessor

      作用:對通過 BeanFactory 創(chuàng)建的 bean 進行屬性填充。

      這是AbstractAutowireCapableBeanFactory#populateBean 方法中的某一段代碼:

      for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}

      我打出了所有要進行輪詢校驗的 BeanPostProcessor

      brokenComponent 中有兩個bean:

      一個是通過@Resource 注解注入的,一個是通過@Autowire 進行注入的,奉勸各位同學千萬不要兩種混用?。。?!


      2.4.1:CommonAnnotationBeanPostProcessor

      /** * … *

      The central element is the {@link javax.annotation.Resource} annotation * for annotation-driven injection of named beans, by default from the containing * Spring BeanFactory, with only {@code mappedName} references resolved in JNDI. * The {@link #setAlwaysUseJndiLookup “alwaysUseJndiLookup” flag} enforces JNDI lookups * equivalent to standard Java EE 5 resource injection for {@code name} references * and default names as well. The target beans can be simple POJOs, with no special * requirements other than the type having to match. * *

      The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too, * analogous to {@link javax.annotation.Resource} but with the capability of creating * specific JAX-WS service endpoints. This may either point to an explicitly defined * resource by name or operate on a locally specified JAX-WS service class. Finally, * this post-processor also supports the EJB 3 {@link javax.ejb.EJB} annotation, * analogous to {@link javax.annotation.Resource} as well, with the capability to * specify both a local bean name and a global JNDI name for fallback retrieval. * The target beans can be plain POJOs as well as EJB 3 Session Beans in this case. * … */public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessorimplements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { …@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (Throwable ex) {throw new BeanCreationException(beanName, “Injection of resource dependencies failed”, ex);}return pvs;} }

      通過翻譯及反復debug驗證,這個類將對某個bean中所有被 @Resource 注解修飾的屬性進行填充。


      2.4.2:AutowiredAnnotationBeanPostProcessor

      /** * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that autowires annotated fields, setter methods and arbitrary config methods. * Such members to be injected are detected through a Java 5 annotation: by default, * Spring’s {@link Autowired @Autowired} and {@link Value @Value} annotations. * *

      Also supports JSR-330’s {@link javax.inject.Inject @Inject} annotation, * if available, as a direct alternative to Spring’s own {@code @Autowired}. * … * @see Autowired * @see Value */public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapterimplements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { …@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, “Injection of autowired dependencies failed”, ex);}return pvs;}}

      這個類將對某個bean中所有被 @Autowire@Value 注解修飾的屬性進行填充。


      2.4.3:@Autowire VS @Resource

      1:無論使用@Resource 還是 @Resource,都不影響bean的初始化順序。

      2:@Resource修飾的屬性 要優(yōu)先于 @Autowire修飾的屬性 進行初始化。

      2.5:報錯流程

      1: getBean(“brokenComponent”) -> getSingleton(“brokenComponent”) -> beforeSingletonCreation(“brokenComponent”) -> createBean(“brokenComponent”) -> addSingletonFactory(“brokenComponent”) -> inject() ->2: getBean(“tubunComponent”,TubunComponent.class) -> getSingleton(“tubunComponent”) -> beforeSingletonCreation(“tubunComponent”) -> 3:getBean(“twConfig”) -> getSingleton(“twConfig”) -> beforeSingletonCreation(“twconfig”) -> addSingletonFactory(“twConfig”) -> afterSingletonCreation(“twConfig”)4:getBean(“denpaComponent”) -> getSingleton(“denpaComponent”) -> beforeSingletonCreation(“denpaComponent”) -> createBean(“denpaComponent”) ->addSingletonFactory(“denpaComponent”) -> inject() ->5:getBean(“tolenComponent”) -> getSingleton(“tolenComponent”) -> beforeSingletonCreation(“tolenComponent”) -> createBean(“tolenComponent”) ->addSingletonFactory(“tolenComponent”) -> inject() ->6:getBean(“tubunComponent”,TubunComponent.class) -> getSingleton(“tubunComponent”) -> beforeSingletonCreation(“tubunComponent”) -> 報錯7:afterSingletonCreation(“tolenComponent”) -> afterSingletonCreation(“denpaComponent”) -> afterSingletonCreation(“tubunComponent”) -> afterSingletonCreation(“brokenComponent”)

      可以看到 tubunComponent 進行了兩次 getSingleton,經過循環(huán)依賴檢查的時候報錯了。


      2.6:錯誤流程分析歸納

      1:Spring 容器先加載 brokenComponent 這個 bean。

      2:brokenComponent依賴了tubunComponent(@Resource修飾),因此優(yōu)先填充 tubunComponent,因此去初始化tubunComponent 這個bean,并將tubunComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。

      3:tubunComponent依賴了depenComponent,因此去初始化depenComponent 這個bean,并將depenComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。

      4:depenComponent依賴了tolenComponent,因此去初始化depenComponent 這個bean,并將tolenComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中。

      5:tolenComponent依賴了tubunComponent,因此去初始化tubunComponent 這個bean,然而當將tubunComponent 存放在 DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation 集合中時發(fā)現(xiàn) tubunComponent 已經存在了,從而判斷這幾個bean 產生了循環(huán)依賴并跑出異常。


      beforeSingletonCreation(String beanName) 方法源碼:

      /** * Callback before singleton creation. *

      The default implementation register the singleton as currently in creation. * @param beanName the name of the singleton about to be created * @see #isSingletonCurrentlyInCreation */protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}}


      2.7:解決方式

      1:使用@Lazy 修飾。

      2:這其實是一個操作不當造成的問題,因為spring的三級緩存已經解決了set注入導致的循環(huán)依賴,而這幾個bean并不是全部都是使用構造器注入。

      在 brokeComponent 中使用@Autowired修飾了tolenComponent,又用@Resource 修飾了tubunComponent,導致tubunComponent要優(yōu)先于tolenComponent優(yōu)先加載,而tolenComponent 又依賴了tubunComponent,因此報錯。我們只需要讓tolenComponent 優(yōu)先于 tubunComponent 優(yōu)先加載就可以了。

      實驗證明:

      1:@Resource 修飾 tolenComponent + @Resource 修飾 tubunComponent 的組合不會報錯。2:@Resource 修飾 tolenComponent + @Autowired 修飾 tubunComponent 的組合不會報錯。3:@Autowired 修飾 tolenComponent + @Autowired 修飾 tubunComponent 的組合不會報錯。

      偏偏使用了會報錯的一種組合……

      鄭重聲明:本文內容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。
      用戶投稿
      上一篇 2022年7月26日 15:13
      下一篇 2022年7月26日 15:13

      相關推薦

      聯(lián)系我們

      聯(lián)系郵箱:admin#wlmqw.com
      工作時間:周一至周五,10:30-18:30,節(jié)假日休息