zoukankan      html  css  js  c++  java
  • 【Spring】循环依赖 Java Vs Spring

    菜瓜:水稻,这次我特意去看了java的循环依赖

    水稻:哟,有什么收获

    菜瓜:两种情况,构造器循环依赖,属性循环依赖

    • 构造器循环依赖在逻辑层面无法通过。对象通过构造函数创建时如果需要创建另一个对象,就会存在递归调用。栈内存直接溢出
    • 属性循环依赖可以解决。在对象创建完成之后通过属性赋值操作。
    • package club.interview.base;
      
      /**
       * 构造器循环依赖 - Exception in thread "main" java.lang.StackOverflowError
       * toString()循环打印也会异常 - Exception in thread "main" java.lang.StackOverflowError
       * @author QuCheng on 2020/6/18.
       */
      public class Circular {
      
          class A {
              B b;
      
      //        public A() {
      //            b = new B();
      //        }
      
      //        @Override
      //        public String toString() {
      //            return "A{" +
      //                    "b=" + b +
      //                    '}';
      //        }
          }
      
          class B {
              A a;
      
      //        public B() {
      //            a = new A();
      //        }
      
      //        @Override
      //        public String toString() {
      //            return "B{" +
      //                    "a=" + a +
      //                    '}';
      //        }
          }
      
          private void test() {
              B b = new B();
              A a = new A();
              a.b = b;
              b.a = a;
              System.out.println(a);
              System.out.println(b);
          }
      
          public static void main(String[] args) {
              new Circular().test();
          }
      }

    水稻:厉害啊,Spring也不支持构造函数的依赖注入,而且也不支持多例的循环依赖。同样的,它支持属性的依赖注入。

    • 看效果 - 如果toString()打印同样会出现栈内存溢出。
    • package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("a")
      public class CircularA {
      
          @Resource
          private CircularB circularB;
      
      //    @Override
      //    public String toString() {
      //        return "CircularA{" +
      //                "circularB=" + circularB +
      //                '}';
      //    }
      }
      
      
      package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("b")
      public class CircularB {
      
          @Resource
          private CircularA circularA;
      
      //    @Override
      //    public String toString() {
      //        return "CircularB{" +
      //                "circularA=" + circularA +
      //                '}';
      //    }
      }
      
      
          @Test
          public void testCircular() {
              String basePackages = "com.vip.qc.circular";
              new AnnotationConfigApplicationContext(basePackages);
          }

    菜瓜:看来spring的实现应该也是通过属性注入的吧

    水稻:你说的对。先给思路和demo,之后带你扫一遍源码,follow me !

    • spring的思路是给已经初始化的bean标记状态,假设A依赖B,B依赖A,先创建A
      • 先从缓存容器(总共三层,一级拿不到就拿二级,二级拿不到就从三级缓存中拿正在创建的)中获取A,未获取到就执行创建逻辑
      • 对象A在创建完成还未将属性渲染完之前标记为正在创建中,放入三级缓存容器。渲染属性populateBean()会获取依赖的对象B。
      • 此时B会走一次getBean逻辑,B同样会先放入三级缓存,然后渲染属性,再次走getBean逻辑注入A,此时能从三级缓存中拿到A,并将A放入二级容器。B渲染完成放入一级容器
      • 回到A渲染B的方法populateBean(),拿到B之后能顺利执行完自己的创建过程。放入一级缓存
    •  为了证实结果,我把源码给改了一下,看结果

      • package com.vip.qc.circular;
        
        import org.springframework.stereotype.Component;
        
        import javax.annotation.Resource;
        
        /**
         * @author QuCheng on 2020/6/18.
         */
        @Component("a")
        public class CircularA {
        
            @Resource
            private CircularB circularB;
        
            @Override
            public String toString() {
                return "CircularA{" +
                        "circularB=" + circularB +
                        '}';
            }
        }
        
        
        package com.vip.qc.circular;
        
        import org.springframework.stereotype.Component;
        
        import javax.annotation.Resource;
        
        /**
         * @author QuCheng on 2020/6/18.
         */
        @Component("b")
        public class CircularB {
        
            @Resource
            private CircularA circularA;
        
            @Override
            public String toString() {
                return "CircularB{" +
                        "circularA=" + circularA +
                        '}';
            }
        }
        
        
        测试代码
        @Test
            public void testCircular() {
                String basePackages = "com.vip.qc.circular";
                new AnnotationConfigApplicationContext(basePackages);
        }
        
        测试结果(我改过源码了)
        ---- 
        将a放入三级缓存
        将b放入三级缓存
        将a放入二级缓存
        将b放入一级缓存
        从二级缓存中拿到了a
        将a放入一级缓存
          
    • 再看源码
      • 关键类处理getSingleton逻辑 - 缓存容器
        • public class DefaultSingletonBeanRegistry 
          
            /** Cache of singleton objects: bean name to bean instance. */
            // 一级缓存
              private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
          
              /** Cache of singleton factories: bean name to ObjectFactory. */
            // 三级缓存
              private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
          
              /** Cache of early singleton objects: bean name to bean instance. */
            // 二级缓存
              private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
      • 主流程 AbstractApplicationContext#refresh() -> DefaultListableBeanFactory#preInstantiateSingletons() -> AbstractBeanFactory#getBean() & #doGetBean()
        • protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
             /**
              * 处理FactoryBean接口名称转换 {@link BeanFactory#FACTORY_BEAN_PREFIX }
              */
             final String beanName = transformedBeanName(name);
                  ...
             // ①从缓存中拿对象(如果对象正在创建中且被依赖注入,会放入二级缓存)
             Object sharedInstance = getSingleton(beanName);
             if (sharedInstance != null && args == null) {
                ...
             }else {      
                      ...
                   if (mbd.isSingleton()) {
                     // ② 将创建的对象放入一级缓存
                      sharedInstance = getSingleton(beanName, () -> {
                         try {
                               // ③ 具体创建的过程,每个bean创建完成之后都会放入三级缓存,然后渲染属性
                            return createBean(beanName, mbd, args);
                         }catch (BeansException ex) {
                           ...
             ...
             return (T) bean;
          } 
      • ①getSingleton(beanName) - 二级缓存操作
        • protected Object getSingleton(String beanName, boolean allowEarlyReference) {
             // 实例化已经完成了的放在singletonObjects
             Object singletonObject = this.singletonObjects.get(beanName);
             // 解决循环依赖
             if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                   singletonObject = this.earlySingletonObjects.get(beanName);
                   if (singletonObject == null && allowEarlyReference) {
                      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                      if (singletonFactory != null) {
                         singletonObject = singletonFactory.getObject();
                         this.earlySingletonObjects.put(beanName, singletonObject);
                         if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                            System.out.println("将"+beanName+"放入二级缓存");;
                         this.singletonFactories.remove(beanName);
                      }
                   }else if(singletonObject != null){
                      System.out.println("从二级缓存中拿到了"+beanName);
                   }
                }
             }
             return singletonObject;
          }
      • ② getSingleton(beanName,lamdba) - 一级缓存操作
        • public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
                Assert.notNull(beanName, "Bean name must not be null");
                synchronized (this.singletonObjects) {
                   Object singletonObject = this.singletonObjects.get(beanName);
                   if (singletonObject == null) {
                      if (this.singletonsCurrentlyInDestruction) {
                      ...
                      // 正在创建的bean加入singletonsCurrentlyInCreation - 保证只有一个对象创建,阻断循环依赖
                      beforeSingletonCreation(beanName);
                                ...
                      try {
                         singletonObject = singletonFactory.getObject();
                      ...
                      finally {
                      ...
                         // 从singletonsCurrentlyInCreation中移除
                         afterSingletonCreation(beanName);
                      }
                      if (newSingleton) {
                         // 对象创建完毕 - 放入一级缓存(从其他缓存移除)
                         addSingleton(beanName, singletonObject);
                      }
                   }
                   return singletonObject;
                }
             }
                  
           //  -----  内部调用一级缓存操作
              protected void addSingleton(String beanName, Object singletonObject) {
                  synchronized (this.singletonObjects) {
                      if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                          System.out.println("将"+beanName+"放入一级缓存");;
                      this.singletonObjects.put(beanName, singletonObject);
                      this.singletonFactories.remove(beanName);
                      this.earlySingletonObjects.remove(beanName);
                      this.registeredSingletons.add(beanName);
                  }
              }        
             
      • ③createBean(beanName, mbd, args) - 三级缓存操作
        • protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
               ...
             if (instanceWrapper == null) {
                // 5* 实例化对象本身
                instanceWrapper = createBeanInstance(beanName, mbd, args);
             }
             ...
             boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                   isSingletonCurrentlyInCreation(beanName));
             if (earlySingletonExposure) {
                ...
                // 将创建好还未渲染属性的bean 放入三级缓存
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
             }
          
             Object exposedObject = bean;
             try {
                // 渲染bean自身和属性
                populateBean(beanName, mbd, instanceWrapper);
                // 实例化之后的后置处理 - init
                exposedObject = initializeBean(beanName, exposedObject, mbd);
             }
             catch (Throwable ex) {
             ...
             return exposedObject;
          }
            
            
             // ------------- 内部调用三级缓存操作 
             protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
                  Assert.notNull(singletonFactory, "Singleton factory must not be null");
                  synchronized (this.singletonObjects) {
                      if (!this.singletonObjects.containsKey(beanName)) {
                          if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                              System.out.println("将"+beanName+"放入三级缓存");;
                          this.singletonFactories.put(beanName, singletonFactory);
                          this.earlySingletonObjects.remove(beanName);
                          this.registeredSingletons.add(beanName);
                      }
                  }
              }       

    菜瓜:demo比较简单,流程大致明白,源码我还需要斟酌一下,整体有了概念。这个流程好像是掺杂在bean的创建过程中,结合bean的生命周期整体理解可能会更深入一点

    水稻:是的。每个知识点都不是单一的,拿着bean的生命周期再理解一遍可能会更有收获。

    讨论

    • 为什么是三级缓存,两级不行吗?
      • 猜测:理论上两级也可以实现。多一个二级缓存可能是为了加快获取的速度。假如A依赖B,A依赖C,B依赖A,C依赖A,那么C在获取A的时候只需要从二级缓存中就能拿到A了

    总结

    • Spring的处理方式和java处理的思想一致,构造器依赖本身是破坏语义和规范的
    • 属性赋值--> 依赖注入 。 先创建对象,再赋值属性,赋值的时候发现需要创建便生成依赖对象,被依赖对象需要前一个对象就从缓存容器中拿取即可
  • 相关阅读:
    java实验报告(实验五)
    java实验报告(实验三)
    java读书笔记二
    总结报告
    Android实践项目汇报(总结)-修改
    Android实践项目汇报(总结)
    Android实践项目汇报(四)
    Android实践项目汇报(三)
    Android实践项目汇报(二)
    Android实践项目汇报-改(一)
  • 原文地址:https://www.cnblogs.com/nightOfStreet/p/13160115.html
Copyright © 2011-2022 走看看