天天看点

这么回答【循环依赖】助力轻松拿下阿里P6

这么回答【循环依赖】助力轻松拿下阿里P6

一篇文章彻底搞定Spring循环依赖

lecture:波哥

一、什么是循环依赖

看下图:

这么回答【循环依赖】助力轻松拿下阿里P6

  上图是循环依赖的三种情况,虽然方式有点不一样,但是循环依赖的本质是一样的,就你的完整创建要依赖与我,我的完整创建也依赖于你。相互依赖从而没法完整创建造成失败。

二、构造注入

1. 案例演示

  构造注入就是依赖关系在构造方法中关联。

@Component
@Scope(value = "prototype")
public class BeanA {

    private BeanB beanB;

    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Component
public class BeanB {
    private BeanA beanA;

    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}      

  然后我们可以通过启动容器来查看,在Spring中构造注入的​

​循环依赖​

​在Spring中是没有办法解决的。只是通过抛出异常来处理了。

@Configuration
@ComponentScan
public class Demo1Main {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(Demo1Main.class);
    }
}      
这么回答【循环依赖】助力轻松拿下阿里P6

2.解决方案

  那么在Spring中是如何发现实例的Bean对象有​

​循环依赖​

​的问题,并抛出异常的呢?其实实现逻辑也比较简单,如下图:

这么回答【循环依赖】助力轻松拿下阿里P6

  实现的过程也很简单,就是在创建对象之前判断该对象在这个临时容器中是否存在,如果不存在就开始创建,如果存在则说明产生了​

​循环依赖​

​。对象创建完成后就把该对象从容器中移除。对应的看看Spring的源码中的处理。

这么回答【循环依赖】助力轻松拿下阿里P6

3.单例分析

  先来看单例的处理,进入到​

​getSingleton​

​​方法中。我们需要关注的是两个方法​

​beforeSingletonCreation(beanName)​

​​和​

​afterSingletonCreation(beanName);​

这么回答【循环依赖】助力轻松拿下阿里P6
这么回答【循环依赖】助力轻松拿下阿里P6

beforeSingletonCreation方法

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

上面的代码很清楚的看到,如果已经存在就抛出循环依赖的错误。而且因为是单例,所以容器也没有通过​

​ThreadLocal​

​来处理了。

对应的​

​afterSingletonCreation​

​方法就是移走了。

protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
  }      

4.原型分析

  原型Bean思路差不多,但是实现方式区别有点大,我们先来看看​

​beforePrototypeCreation(beanName)​

​​和​

​afterPrototypeCreation(beanName);​

​这两个方法。

// 数据存储在了 ThreadLocal 中
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
      new NamedThreadLocal<>("Prototype beans currently in creation");

protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
      this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String) {
      Set<String> beanNameSet = new HashSet<>(2);
      beanNameSet.add((String) curVal);
      beanNameSet.add(beanName);
      this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
      Set<String> beanNameSet = (Set<String>) curVal;
      beanNameSet.add(beanName);
    }
  }      
protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
      this.prototypesCurrentlyInCreation.remove();
    }
    else if (curVal instanceof Set) {
      Set<String> beanNameSet = (Set<String>) curVal;
      beanNameSet.remove(beanName);
      if (beanNameSet.isEmpty()) {
        this.prototypesCurrentlyInCreation.remove();
      }
    }
  }      

可以看到这两个方法都只是在​

​ThreadLocal​

​中错了BeanName的写入和移除的操作。并没有做相关的检查,而这个检查其实是在

这么回答【循环依赖】助力轻松拿下阿里P6

这样一来Spring中的构造注入的循环依赖针对单例或是原型的处理我们就清楚了!

三、依赖注入

1.案例演示

  设值注入就是通过setter方法来完成属性的赋值。

@Component
public class UserD {

    @Autowired
    private UserC userC;
}

@Component
public class UserC {
    @Autowired
    private UserD userD;

}      

写主方法测试

@Configuration
@ComponentScan
public class Demo2Main {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(Demo2Main.class);
        System.out.println(ac.getBean(UserC.class));
    }
}      

我们可以看到在单例的情况下,Spring解决了​

​循环依赖​

​的问题了。

这么回答【循环依赖】助力轻松拿下阿里P6

但是在两个对象都是原型的情况下就解决不了了。

@Component
@Scope(value = "prototype")
public class UserC {
    @Autowired
    private UserD userD;
    
}

@Component
@Scope(value = "prototype")
public class UserD {

    @Autowired
    private UserC userC;
}      

启动测试我们可以看到出现了循环依赖的错误提示了

这么回答【循环依赖】助力轻松拿下阿里P6

当然,如果我们给其中的一个设置为单例,那么循环依赖问题即可解决。原理,我们分析下就清楚了。

2.解决方案

  针对依赖注入这种情况下产生的​

​循环依赖​

​​问题,我们的解决方案是​

​提前暴露​

​。

这么回答【循环依赖】助力轻松拿下阿里P6

  但是在Spring中对于Bean实例因为AOP的存在,我们可能返回的并不是原始的Bean对象,而是被AOP增强的代理对象,这时在Spring中需要使用到三级缓存来处理。我们具体来看看Spring中的处理方案。

3.原理分析

  然后我们来具体看看在Spring中对于依赖注入的​

​循环依赖​

​​的解决。进入到​

​AbstractAutowireCapableBeanFactory​

​​的​

​doCreateBean​

​方法中。

这么回答【循环依赖】助力轻松拿下阿里P6

  上面看到了具体创建Bean对象的方法​

​createBeanInstance​

​方法中,然后在下面中比较重要的两个的方法

这么回答【循环依赖】助力轻松拿下阿里P6

  那么对于的​

​提前暴露​

​​应该要在​

​populateBean​

​​方法之前实现。往前面找看到了​

​earlySingletonExposure​

​​变量,标识的就是是否支持​

​提前暴露​

​。

// 判断条件: 1.是否是单例Bean 2.是否支持循环依赖 3.是否是当前正在创建的Bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));      

  如果上面的条件成立就会走下面的代码

if (earlySingletonExposure) {
      // 把 Lambda 表达式存储在了三级缓存中 
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
// 对应的 addSingletonFactory 方法
  protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
            // 该单例对象还未创建过
      if (!this.singletonObjects.containsKey(beanName)) {
                // 把 上面的 Lambda 表达式 存储在了 三级缓存中
        this.singletonFactories.put(beanName, singletonFactory);
                // 对应的二级缓存置空
        this.earlySingletonObjects.remove(beanName);
                // 记录该单例对象名称
        this.registeredSingletons.add(beanName);
      }
    }
  }      
这么回答【循环依赖】助力轻松拿下阿里P6

然后我们需要在AOP创建对应的代理增强对象之后再看是如果来暴露代理对象的。

这么回答【循环依赖】助力轻松拿下阿里P6

也就是我们看的关键是​

​getSingleton​

​方法

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 从一级缓存中获取对象
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 没有获取到创建好的Bean单例,同时当前正在创建Bean对象
        // 从 二级缓存中获取 半层品 对象
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
         // 二级缓存中没有需要获取的Bean 半成品对象
         synchronized (this.singletonObjects) {
            // Consistent creation of early reference within full singleton lock
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 一级缓存中没有
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) {
                   // 二级缓存 没有  就取出之前存储在三级缓存中的 Lambda 表达式
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                      // 根据Lambda表达式 获取到Bean的 代理对象
                     singletonObject = singletonFactory.getObject();
                      // 把Bean的半成品对象存储在二级缓存中
                     this.earlySingletonObjects.put(beanName, singletonObject);
                      // 清除 一级缓存的 对象
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
    // 返回对应的半成品对象
   return singletonObject;
}      

然后对应的获取代理对象的逻辑是在​

​singletonFactory.getObject()​

​中。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean; // 原始Bean
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
                // 通过每个BeanPostProcessor 对Bean对象做后置处理
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
          SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    // 递归获取对应的 代理对象
          exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
      }
    }
        // 返回 对应的增强Bean
    return exposedObject;
  }      

具体逻辑如下图:

这么回答【循环依赖】助力轻松拿下阿里P6

当依赖关系完成后。会进入到前面的​

​getSingleton​

​方法中。

这么回答【循环依赖】助力轻松拿下阿里P6

然后还要做对应的二级缓存和一级缓存数据处理

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
            // 创建好的单例Bean会存储在 singletonObjects 这个Map 中,也就是一级缓存中
      this.singletonObjects.put(beanName, singletonObject);
            // 移除三级缓存的Lambda表达式
      this.singletonFactories.remove(beanName);
            // 移除二级缓存中的Bean的半成品对象,移除提前暴露的对象
      this.earlySingletonObjects.remove(beanName);
            // 注册 单例
      this.registeredSingletons.add(beanName);
    }
  }      

继续阅读