博学谷学习记录 自我总结 用心分享 | Spring源码刨析

发布时间 2023-10-13 17:30:20作者: 刚刚一个

  别再盲目的说spring有三级缓存了,两个缓存只是启动时为了解决循环依赖,spring启动后只有一个缓存有用

一、什么是循环依赖

循环依赖指的就是循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,则它们最终反映为一个环。

不是指循环调用,循环调用是方法之间的环调用,比如

public class TestA {
   private TestB testB;
   public void a(){
      //b方法中testC.c()——>testA.a();
      testB.b();
   }

   public static void main(String[] args) {
      new TestA().a();
   }
}

循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致StackOverflowError,内存溢出错误。

二、Spring 如何解决循环依赖

Spring容器循环依赖包括构造器循环依赖和 setter循环依赖,那 Spring容器如何解决循环呢?(还是用上面的TestA举例)

1.构造器循环依赖

表示通过构造器注入构成的循环依赖,如

此种情况的循环依赖是无法解决的 ,只能抛出 BeanCurrentlylnCreationException异常 。

可以理解为在准备创建 TestA类时(还未创建), 发现构造方法需要 TestB类,那就去创建 TestB, 在创建 TestB类时又发现 需要 TestC类, 则又去创建 TestC, 最终在创建 TestC时发现又需要 TestA,而最开始的TestA并未被创建,无法使用,所以无法解决。

2. setter循环依赖

表示通过 set注入方式构成的循环依赖 。如

 

这种方式的循环依赖能解决是通过 Spring 容器 在创建bean之后还未初始化时 ,就注册一个ObjectFactory类持有该bean(如下面代码所示),从而使其他 bean 在引用到该 bean时,通过其ObjectFactory的getObject方法(这里调用了getEarlyBeanReference方法,可以理解为返回了bean)获取到该bean

 

三、Spring相关源码解析

 

缓存相关类:重点看AbstractBeanFactory父类DefaultSingletonBeanRegistry,涉及的成员变量有

//第一层缓存 k-> beanName  v->bean实例,bean创建并初始化后,会缓存至该map
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

//第二层缓存 k-> beanName  v->ObjectFactory(ObjectFactory其实就是返回bean实例的引用),bean创建后(初始化之前),会缓存至该map,初始化完成后,从该map移除
//循环依赖相关!!!
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

//循环依赖相关!!!
//第三层缓存 k-> beanName  v->bean实例,在获取bean时,如果第一层缓存没有,第二层缓存有,就调用对应ObjectFactory的getObject方法,并将返回的bean缓存至该map,从第二层缓存中移除
//(其实就是怕ObjectFactory.getObject是个耗时操作,为了提高性能,调用一次后将结果缓存),初始化完成后,从该map移除
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

//循环依赖相关!!!“当前创建bean池”,bean创建前,会放到该set中,创建完并初始化后,从set中移除
private final Set<String> singletonsCurrentlyInCreation =
      Collections.newSetFromMap(new ConcurrentHashMap<>(16));

创建bean时会调用如下方法(按调用顺序):

1.1 doGetBean:获取bean

BeanFactory和ApplicationContext的getBean方法(还有注解注入bean)最终都会走到这个方法

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {

   String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   //重点1!!!从缓存中获取bean,getSingleton(beanName, true),源码见下
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      ...
   }

   else {
      ...

      try {
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         ...
         if (mbd.isSingleton()) {
            //重点2,在创建bean的前后做一些处理,源码见下
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //重点3,真正去创建bean,会调用doGetBean,源码见下
                  return createBean(beanName, mbd, args);
               }
               ...
            });
            ...
         }
    ...
   return (T) bean;
}

1.2 getSingleton:缓存中获取单例bean

//获取bean时会先调用该方法
//allowEarlyReference,是否允许提前曝光,即从earlySingletonObjects查ObjectFactory
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //先看有没有实例缓存
   Object singletonObject = this.singletonObjects.get(beanName);
   //为null且singletonsCurrentlyInCreation包含beanName,说明该bean正在创建中,比如创建A时,发现引用了B,去创建B,B又引用了A,这样再去创建A时,singletonsCurrentlyInCreation包含了A的beanName
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         //比如A引用B,B引用A、C,C也引用A,在创建B时,会调用A的ObjectFactory.getObject()方法获取A,后面创建C时,也需要A,则直接从earlySingletonObjects中get
         singletonObject = this.earlySingletonObjects.get(beanName);
         //如果没有缓存singletonObject且是要提前曝光
         if (singletonObject == null && allowEarlyReference) {
             //获取singletonObject
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               //缓存,这样就不用再调用singletonFactory.getObject()方法
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

逻辑就是先从实例缓存里拿,没有且bean“正在创建中”(创建A后,设置属性时需要B,去创建B,给B设置属性时又需要A,去创建A时,A就是正在创建中)就从earlySingletonObjects缓存里拿,还没有就从singletonFactories里拿到ObjectFactory,若有,就调用其getObject方法返回bean,并缓存到earlySingletonObjects中

1.3 getSingleton:获取单例bean

这个方法只是个过渡方法,在实际获取bean之前把beanName添加到“当前创建bean池”中,获取之后再从“当前创建bean池”中移除beanName并添加到实例缓存中

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) {
         ...
         //把beanName加到singletonsCurrentlyInCreation,添加失败就抛BeanCurrentlyInCreationException
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         ...
         finally {
            ...
            //把beanName从singletonsCurrentlyInCreation中移除,移除失败就抛IllegalStateException
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
             //把单例缓存到singletonObjects
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}
protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

1.4 createBean:创建bean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   BeanWrapper instanceWrapper = null;
   ...
   //通过构造器方法反射创建对象,并未初始化,如果设置了构造器引用,如<constructor-arg ref="testb"/>,也会去getBean
   instanceWrapper = createBeanInstance(beanName, mbd, args);
   ...


   //单例,且允许循环依赖,且还在创建中,就需要去解决循环依赖,即放入ObjectFactory
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
       //重点!!!上面介绍过该方法,添加bean对应的ObjectFactory
       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // 设置bean的属性及调用bean的初始化方法
   Object exposedObject = bean;
   try {
       //获取PropertyValues,遍历初始化然后设值
       //会再次调用beanFactory.getBean(refName),比如A中注解引用B,就会调用beanFactory.getBean(BName)获取B实例赋值给A
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   ...
   return exposedObject;
}

四、流程图

五、个人理解部分

假如我来实现循环依赖的解决,可能是如下代码:

public class MySpring {
    //同singletonObjects,缓存beanName和实例
   private Map<String,Object> beanInstanceMap=new HashMap<>();
   //beanName 和BeanDefinition的缓存
   private Map<String, Class> beanDefinationMap=new HashMap<>();
   //模仿BeanDefinition中的PropertyValue
   private Map<String, String> beanPropertyMap=new HashMap<>();

   private void init(){
      //注册bean
      beanDefinationMap.put("testA", TestA.class);
      beanDefinationMap.put("testB", TestB.class);
      beanDefinationMap.put("testC", TestC.class);
      //设置bean之间的引用关系
      beanPropertyMap.put("testA","testB");
      beanPropertyMap.put("testB","testC");
      beanPropertyMap.put("testC","testA");
   }

   private Object getBean(String beanName) throws Exception{
       //1.先从缓存中获取,
      Object instance=beanInstanceMap.get(beanName);
      if (instance==null) {
          //2.为null就去反射创建实例
         instance=beanDefinationMap.get(beanName).newInstance();
         //3.先缓存再去填充bean
         beanInstanceMap.put(beanName,instance);
         //4.拿到配置的属性信息,获取对应bean并设置到bean中
         String property=beanPropertyMap.get(beanName);
         if (!StringUtils.isEmpty(property)){
            Object propertyValue=getBean(property);
            setPropertyValue(instance,property,propertyValue);
         }
      }
      return instance;
   }

   private void setPropertyValue(Object instance, String property, Object propertyValue) throws Exception {
      Field field=instance.getClass().getDeclaredField(property);
      field.setAccessible(true);
      field.set(instance, propertyValue);
   }

   public static void main(String[] args) throws Exception {
      MySpring spring=new MySpring();
      spring.init();
      TestA testA= (TestA) spring.getBean("testA");
      //打印true
      System.out.println(testA.getTestB().getTestC().getTestA()==testA);
   }
}

这里我只使用了一个缓存就解决了循环依赖,所以产生了如下疑问

  1. spring为什么额外还使用了earlySingletonObjects 和singletonFactories 两个map,即套了一层ObjectFactory ? 其实就和FactoryBean一样,允许使用者针对这种情况定制化特殊处理,getEarlyBeanReference方法中遍历SmartInstantiationAwareBeanPostProcessor,将处理结果当作bean,导致可能不是原来的引用
  2. 为什么不创建完了bean就放实例缓存里,而要等装配完成? 因为在initializeBean中,有applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization 方法允许返回别的结果,所以等bean完成全部操作再放入缓存,但是如果在装配时(populateBean)别的bean又引用当前bean,此时实例缓存里没有,不可能再去创建新的,那么就使用中间层ObjectFactory ,虽然不是真正的实例,但是其实差不多,其他bean里的引用是getEarlyBeanReference返回的(其实就是bean),在别的bean创建完成后,当前bean走initializeBean后面的方法,其实exposedObject和bean是一个对象的引用,所以其他bean的里的当前bean也就装配完成了