一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring如何快速穩定解決循環依賴問題

spring如何快速穩定解決循環依賴問題

2021-08-23 11:23morris131 Java教程

這篇文章主要介紹了spring如何快速穩定解決循環依賴問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

循環依賴其實就是循環引用,很多地方都說需要兩個或則兩個以上的bean互相持有對方最終形成閉環才是循環依賴,比如A依賴于B,B依賴于C,C又依賴于A。其實一個bean持有自己類型的屬性也會產生循環依賴。

setter singleton循環依賴

使用

SingleSetterBeanA依賴SingleSetterBeanB,SingleSetterBeanB依賴SingleSetterBeanA。

  1. @Data
  2. public class SingleSetterBeanA {
  3. @Autowired
  4. private SingleSetterBeanB singleSetterBeanB;
  5. }
  1. @Data
  2. public class SingleSetterBeanB {
  3. @Autowired
  4. private SingleSetterBeanA singleSetterBeanA;
  5. }

源碼分析

spring是通過三級緩存來解決循環依賴的,那么三級緩存是怎么工作的呢?

三級緩存對應org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類的三個屬性:

  1. /** Cache of singleton objects: bean name to bean instance. */
  2. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 一級緩存
  3.  
  4. /** Cache of singleton factories: bean name to ObjectFactory. */
  5. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二級緩存
  6.  
  7. /** Cache of early singleton objects: bean name to bean instance. */
  8. private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 三級緩存

spring如何快速穩定解決循環依賴問題

對于setter注入造成的依賴是通過Spring容器提前暴露剛完成實例化但未完成初始化的bean來完成的,而且只能解決單例作用域的bean循環依賴。通過提前暴露一個單例工廠方法,從而使其他bean能引用到該bean,關鍵源碼如下所示:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

  1. // 處理循環依賴,實例化后放入三級緩存
  2. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  3. isSingletonCurrentlyInCreation(beanName));
  4. if (earlySingletonExposure) {
  5. if (logger.isTraceEnabled()) {
  6. logger.trace("Eagerly caching bean '" + beanName +
  7. "' to allow for resolving potential circular references");
  8. }
  9. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  10. }

bean實例化后放入三級緩存中:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory

  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3. synchronized (this.singletonObjects) {
  4. if (!this.singletonObjects.containsKey(beanName)) {
  5. this.singletonFactories.put(beanName, singletonFactory); // 三級緩存
  6. this.earlySingletonObjects.remove(beanName);
  7. this.registeredSingletons.add(beanName);
  8. }
  9. }
  10. }

放入三級緩存中的是ObjectFactory類型的lambda表達式:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

  1. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  2. Object exposedObject = bean;
  3. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  4. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  5. if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
  6. SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
  7. /**
  8. * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference(java.lang.Object, java.lang.String)
  9. */
  10. // 使用AbstractAutoProxyCreator#getEarlyBeanReference創建代理對象
  11. exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
  12. }
  13. }
  14. }
  15. return exposedObject;
  16. }

構造器參數循環依賴

通過構造器注入構成的循環依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環依賴。

使用

  1. @Data
  2. public class SingleConstrutorBeanA {
  3. public SingleConstrutorBeanA(SingleConstrutorBeanB singleConstrutorBeanB) {
  4. }
  5. }
  1. @Data
  2. public class SingleConstrutorBeanB {
  3. public SingleConstrutorBeanB(SingleConstrutorBeanA singleConstrutorBeanA) {
  4. }
  5. }

上面的代碼運行時會拋出如下異常:

... ...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'singleConstrutorBeanB': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:805)
 at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1403)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1245)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:538)
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:329)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
 ... 76 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
 ... 90 more

源碼分析

Spring容器會將每一個正在創建的Bean標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對于創建完畢的Bean將從“當前創建Bean池”中清除掉。

  1. protected void beforeSingletonCreation(String beanName) {
  2. if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  3. throw new BeanCurrentlyInCreationException(beanName);
  4. }
  5. }

spring如何快速穩定解決循環依賴問題

@Lazy打破循環依賴

在上面的例子中只需要在SingleConstrutorBeanA或者SingleConstrutorBeanB的構造方法上面加上@Lazy注解,就會發現不會拋出異常了,這又是為什么呢?

下面假設在SingleConstrutorBeanA的構造方法上面加了@Lazy注解,在構造B時,發現參數A時被@Lazy注解修飾時,那么就不會調用getBean來獲取對象,而是創建了一個代理對象,所以不會構成真正的循環依賴,不會拋出BeanCurrentlyInCreationException異常。

  1. /**
  2. * 處理懶加載對象
  3. * 懶加載返回的又是一個代理對象,不會真正的調用getBean,所以如果構造方法依賴中有循環依賴,那么不會報錯
  4. * @see org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String)
  5. */
  6. Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
  7. descriptor, requestingBeanName);
  8. if (result == null) {
  9. // 調用beanFactory.getBean(beanName)從容器中獲取依賴對象
  10. result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
  11. }
  12. return result;

spring如何快速穩定解決循環依賴問題

setter prototype循環依賴

對于prototype作用域bean,Spring容器無法完成依賴注入,因為Spring容器不進行緩存"prototype"作用域的bean,因此無法提前暴露一個創建中的bean。

使用

  1. @Data
  2. @Component
  3. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  4. public class PrototypeBeanA {
  5. @Autowired
  6. private PrototypeBeanB prototypeBeanB;
  7. }
  1. @Data
  2. @Component
  3. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  4. public class PrototypeBeanB {
  5. @Autowired
  6. private PrototypeBeanA prototypeBeanA;
  7. }
  1. @Test
  2. public void test3() {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  4. applicationContext.register(PrototypeBeanA.class);
  5. applicationContext.register(PrototypeBeanB.class);
  6. applicationContext.refresh();
  7. applicationContext.getBean(PrototypeBeanA.class); // 此時必須要獲取Spring管理的實例,因為現在scope="prototype" 只有請求獲取的時候才會實例化對象
  8. }

運行結果如下:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:269)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1322)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:668)
 ... 89 more

源碼分析

spring如何快速穩定解決循環依賴問題

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

  1. ... ...
  2. // 判斷是否在當前創建Bean池中
  3. if (isPrototypeCurrentlyInCreation(beanName)) {
  4. throw new BeanCurrentlyInCreationException(beanName);
  5. }
  6. ... ...

異常就是在上面的代碼中拋出來的,那么beanName是什么時候添加至當前創建Bean池中的呢?

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

  1. else if (mbd.isPrototype()) {
  2. // It's a prototype -> create a new instance.
  3. // prototype類型的bean的實例化
  4. Object prototypeInstance = null;
  5. try {
  6. beforePrototypeCreation(beanName);
  7. prototypeInstance = createBean(beanName, mbd, args);
  8. }
  9. finally {
  10. afterPrototypeCreation(beanName);
  11. }
  12. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  13. }

org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation

  1. protected void beforePrototypeCreation(String beanName) {
  2. // ThreadLocal
  3. Object curVal = this.prototypesCurrentlyInCreation.get();
  4. if (curVal == null) {
  5. this.prototypesCurrentlyInCreation.set(beanName);
  6. }
  7. else if (curVal instanceof String) {
  8. Set<String> beanNameSet = new HashSet<>(2);
  9. beanNameSet.add((String) curVal);
  10. beanNameSet.add(beanName);
  11. this.prototypesCurrentlyInCreation.set(beanNameSet);
  12. }
  13. else {
  14. Set<String> beanNameSet = (Set<String>) curVal;
  15. beanNameSet.add(beanName);
  16. }
  17. }

其根本原因就是Spring容器不會對prototype類型的bean進行緩存,因此無法提前利用三級緩存暴露一個代理對象。

循環依賴開關

可以通過allowCircularReferences來禁止循環依賴,這樣的話,singleton bean的setter循環依賴也會報錯。

  1. applicationContext.setAllowCircularReferences(false);

二級緩存可行?

緩存 說明
singletonObjects 第一級緩存,存放可用的成品Bean。
earlySingletonObjects 第二級緩存,存放半成品的Bean,半成品的Bean是已創建對象,但是未注入屬性和初始化,用以解決循環依賴。
singletonFactories 第三級緩存,存的是Bean工廠對象,用來生成半成品的Bean并放入到二級緩存中,用以解決循環依賴。

理論上二級緩存時可行的,只需要將三級緩存中BeanFactory創建的對象提前放入二級緩存中,這樣三級緩存就可以移除了。

那么spring中為什么還要使用三級緩存呢?如果要使用二級緩存解決循環依賴,意味著所有Bean在實例化后就要完成AOP代理,這樣違背了Spring設計的原則,Spring在設計之初就是通過AnnotationAwareAspectJAutoProxyCreator這個后置處理器來在Bean生命周期的最后一步來完成AOP代理,而不是在實例化后就立馬進行AOP代理。

到此這篇關于spring如何快速穩定解決循環依賴問題的文章就介紹到這了,更多相關spring循環依賴內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/u022812849/article/details/114264411

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产福利不卡视频 | 白虎喷水 | 色天使亚洲综合在线观看 | 日本人做受全过程视频 | 性xxxx直播放免费 | 婷婷在线成人免费观看搜索 | 日韩视频在线精品视频免费观看 | 四虎成人国产精品视频 | 欧美精选欧美极品 | 青青热久免费精品视频精品 | 清纯漂亮女友初尝性过程 | 国产福利兔女郎在线观看 | 菠萝视频在线完整版 | 好大好热| 色综合久久日韩国产 | 娇妻被又大又粗又长又硬好爽 | 亚洲琪琪 | 色老大在线 | 黑帮大佬与我的365天2标清中文 | 亚洲AV无码乱码国产麻豆穿越 | 猫咪社区在线播放 | 2018av在线 | 午夜办公室在线观看高清电影 | 久久国产精品福利影集 | 四虎最新永久在线精品免费 | 亚洲 欧美 国产 日韩 字幕 | 2021福利视频| 亚洲欧美专区精品久久 | 亚洲乱亚洲乱妇41p 亚洲乱码一区二区三区国产精品 | 婷婷久久综合九色综合九七 | 国产伦精品一区二区 | 欧美丰满大乳大屁在线观看股 | 欧美在线观看视频一区 | 情侣宾馆愉拍自拍视频 | 日本国产一区二区三区 | 亚洲欧美一区二区三区不卡 | 亚洲国产日韩成人综合天堂 | 日本一区二区三区视频在线观看 | 美女脱了内裤张开腿亲吻男生 | 国产在线激情视频 | 亚洲国产欧美在线成人aaaa |