Java教程

Spring IoC 源码刨析扩展之 循环依赖与三级缓存

本文主要是介绍Spring IoC 源码刨析扩展之 循环依赖与三级缓存,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

  • 一、循环依赖相关定义
    • 1、什么是循环依赖?
    • 2、Spring 中循环依赖场景有哪些?
  • 二、循环依赖处理机制
    • 1、有哪些条件下不支持处理?
    • 2、prototype 原型 bean 的循环依赖无法支持处理原因
      • 1)事先判断排除
      • 2)原型 bean 在创建完成之后不会保存在容器当中
      • 总结:Spring 不支持原型 bean 的循环依赖。
    • 3、单例 bean 循环依赖的处理机制
  • 三、源码刨析,三级缓存
    • 1、三级缓存有哪些?
    • 2、循环依赖时,源码跟踪三级缓存
      • 1)源码跟踪前准备
      • 2)源码跟踪

一、循环依赖相关定义

1、什么是循环依赖?

循环依赖其实就是循环引用,也就是两个或者两个以上的 Bean 互相持有对方,最终形成闭环。
比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A。

循环依赖图

注意:这里不是函数的循环调用,是对象的相互依赖关系。
循环调用其实就是一个死循环,除非有终结条件。

2、Spring 中循环依赖场景有哪些?

Spring 中循环依赖场景有:

  • 构造器的循环依赖(构造器注入)
  • Field 属性的循环依赖(set 注入)

其中,构造器的循环依赖问题无法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决
属性循环依赖时,spring 采用的是提前暴露对象的方法。

二、循环依赖处理机制

1、有哪些条件下不支持处理?

不支持处理条件的有:

  1. 单例 bean 构造器参数循环依赖(无法解决)
  2. prototype 原型 bean 循环依赖(无法解决)

对于 prototype 原型 bean 的初始化过程中不论是通过构造器参数循环依赖还是通过 setXxx 方法产生循环依赖,Spring 都会直接报错处理。

2、prototype 原型 bean 的循环依赖无法支持处理原因

查看源码进行分析。

1)事先判断排除

AbstractBeanFactory.doGetBean() 方法:

// 如果是 prototype 类型且开启允许循环依赖,则抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

AbstractBeanFactory#isPrototypeCurrentlyInCreation

protected boolean isPrototypeCurrentlyInCreation(String beanName) {
   Object curVal = this.prototypesCurrentlyInCreation.get();
   return (curVal != null &&
         (curVal.equals(beanName) || (curVal instanceof Set 
                 && ((Set<?>) curVal).contains(beanName))));
}

在获取 bean 之前如果这个原型 bean 正在被创建则直接抛出异常。

2)原型 bean 在创建完成之后不会保存在容器当中

原型 bean 在创建之前会进行标记这个 beanName 正在被创建,等创建结束之后会删除标记。

AbstractBeanFactory#doGetBean

try {
   // 创建原型 bean 之前添加标记
   beforePrototypeCreation(beanName);
   // 创建原型 bean
   prototypeInstance = createBean(beanName, mbd, args);
} finally {
   // 创建原型 bean 之后删除标记
   afterPrototypeCreation(beanName);
}

总结:Spring 不支持原型 bean 的循环依赖。

3、单例 bean 循环依赖的处理机制

1)单例 bean 通过 setXxx 或者 @Autowired 进行循环依赖。

  • Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。
  • Spring 通过 setXxx 或者 @Autowired 方法解决循环依赖其实是通过提前暴露一个 ObjectFactory 对象来完成的,简单来说 ClassA 在调用构造器完成对象初始化之后,在调用 ClassA 的 setClassB 方法之前就把 ClassA 实例化的对象通过 ObjectFactory 提前暴露到 Spring 容器中。

2)Spring 容器初始化 ClassA 通过构造器初始化对象后提前暴露到 Spring 容器。

AbstractAutowireCapableBeanFactory#doCreateBean

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   // 将初始化后的对象提前以 ObjectFactory 对象注入到容器中
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

3)ClassA 调用 setClassB 方法,Spring 首先尝试从容器中获取 ClassB,此时 ClassB 不存在 Spring 容器中。

4)Spring 容器初始化 ClassB,同时也会将 ClassB 提前暴露到 Spring 容器中。

5)ClassB 调用 setClassA 方法,Spring 从容器中获取 ClassA ,因为第一步中已经提前暴露了 ClassA,因此可以获取到 ClassA 实例。

6)ClassA 通过 Spring 容器获取到 ClassB,完成了对象初始化操作。

7)这样 ClassA 和 ClassB 都完成了对象初始化操作,解决了循环依赖问题。

三、源码刨析,三级缓存

提示:如果有还不熟悉 Spring IoC 源码的朋友请优先查看 Spring IoC 源码刨析 。

1、三级缓存有哪些?

三级缓存主要是在类 DefaultSingletonBeanRegistry 当中。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry 
        implements SingletonBeanRegistry {

    /** 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);
}
  1. 一级缓存对象 singletonObjects, 是 Spring 存储完整的目标对象。
  2. 二级缓存对象 earlySingletonObjects,是 Spring 临时存储的目标对象。
  3. 三级缓存对象 singletonFactories,是目标对象还没真正创建时,Spring 临时存储目标工厂对象。

2、循环依赖时,源码跟踪三级缓存

1)源码跟踪前准备

applicationContext.xml

<!--循环依赖问题-->
<bean id="lagouBean" class="com.lagou.edu.LagouBean">
   <property name="ItBean" ref="itBean"/>
</bean>
<bean id="itBean" class="com.lagou.edu.ItBean">
   <property name="LagouBean" ref="lagouBean"/>
</bean>

实体类略。

2)源码跟踪

step1:先查看获取 lagouBean 实例化对象时,

AbstractBeanFactory#doGetBean 方法中。

// 创建单例 bean
sharedInstance = getSingleton(beanName, () -> {
   try {
      // 创建 bean
      return createBean(beanName, mbd, args);
   } catch (BeansException ex) {
      destroySingleton(beanName);
      throw ex;
   }
});

step2:lagouBean 对象实例化创建。

AbstractAutowireCapableBeanFactory#doCreateBean

if (instanceWrapper == null) {
   // 创建 Bean 实例,仅仅调用构造方法,但是尚未设置属性
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();

step3:合成工厂对象 lagouBeanFactory,并存储在三级缓存当中。

继续在 AbstractAutowireCapableBeanFactory#doCreateBean 当中。

// 将初始化后的对象提前以 ObjectFactory 对象注入到容器中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

DefaultSingletonBeanRegistry#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)) {
            // 存储三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

step4:lagouBean 填充属性 itBean。

继续在 AbstractAutowireCapableBeanFactory#doCreateBean 当中。

// Bean属性填充
populateBean(beanName, mbd, instanceWrapper);

之后跟踪到调用 AbstractBeanFactory.getBean 方法来获取 itBean对象,
具体跟踪路径是:
AbstractAutowireCapableBeanFactory#applyPropertyValues -->
AbstractAutowireCapableBeanFactory#applyPropertyValues -->
BeanDefinitionValueResolver#resolveValueIfNecessary -->
BeanDefinitionValueResolver#resolveReference -->
AbstractBeanFactory#getBean

step5:与 step1step2step3step4 步骤相同,只是目标对象是 itBean。

  1. 创建实例化对象 itBean。
  2. 合成工厂对象 itBeanFactory,并存储在三级缓存当中。
  3. itBean 填充属性 lagouBean。

step6:itBean 在填充属性 lagouBean 过程中,会在三级缓存当中获取到 lagouBeanFactory,并用其获取 lagouBean 对象,存储在二级缓存当中,删除三级缓存。

在 AbstractBeanFactory#doGetBean 方法当中:

// 单纯理解尝试从缓存中获取 bean
Object sharedInstance = getSingleton(beanName);

DefaultSingletonBeanRegistry#getSingleton

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 首先在一级缓存当中获取对象
    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);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

step7:回到 step5 最后,itBean 填充属性 lagouBean 完成。
再回到 itBean 对象的 step1,完成后的 itBean 实例化对象进入了 getSingleton 方法当中。
获取 itBean 对象,存入一级缓存,删除三级缓存。

DefaultSingletonBeanRegistry#getSingleton

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) {
                throw new BeanCreationNotAllowedException(beanName,
                       "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            // 验证完要真正开始创建对象,先标识该 bean 正在被创建,因为 spingbean 创建过程复杂,步骤很多,需要标识
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 传进来的调用,lamda 表达式使用
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            } catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            } finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

DefaultSingletonBeanRegistry#addSingleton

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);
   }
}

step8:最后回到 lagouBean 的 step1 步骤,再次带着 完成后的 lagouBean 实例化对象进入 getSingleton 方法当中。
获取对象,存入一级缓存,删除二级缓存。

结束

文章内容输出来源:拉勾教育Java高薪训练营;

这篇关于Spring IoC 源码刨析扩展之 循环依赖与三级缓存的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!