Spring三级缓存处理循环依赖

Spring是如何解决循环依赖-如下图

在这里插入图片描述

  1. 循环依赖主要分为这三种,自身依赖于自身、互相循环依赖、多组循环依赖。;
  2. 但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖
    于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失
    败。;
  3. 所以需要 Spring 提供了除了构造函数注入和原型注入外的,setter 循环依赖注入
    解决方案;

设计

按照 Spring 框架的设计,用于解决循环依赖需要用到三个缓存,这三个缓存
分别存放了成品对象、半成品对象(未填充属性值)、代理对象,分阶段存放对象内
容,来解决循环依赖问题。
我们需要知道一个核心的原理,就是用于解决循环依赖就必须是三
级缓存呢,二级行吗?一级可以不?其实都能解决,只不过 Spring 框架的实
现要保证几个事情,如只有一级缓存处理流程没法拆分,复杂度也会增加,同
时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来也
更加优雅、简单、易扩展。另外 Spring 的两大特性中不仅有 IOC 还有 AOP,
也就是基于字节码增强后的方法,该存放到哪,而三级缓存最主要,要解决的
循环依赖就是对 AOP 的处理,但如果把 AOP 代理对象的创建提前,那么二级
缓存也一样可以解决。但是,这就违背了 Spring 创建对象的原则,Spring 更
喜欢把所有的普通 Bean 都初始化完成,在处理代理对象的初始化。
不过,没关系我们可以先尝试仅适用一级缓存来解决循环依赖,通过这样的方
式从中学习到处理循环依赖的最核心原来。
在这里插入图片描述

  • 如果仅以一级缓存解决循环依赖,那么在实现上可以通过在 A 对象 newInstance
    创建且未填充属性后,直接放入缓存中。
  • 在 A 对象的属性填充 B 对象时,如果缓存中不能获取到 B 对象,则开始创建 B
    对象,同样创建完成后,把 B 对象填充到缓存中去。
  • 接下来就开始对 B 对象的属性进行填充,恰好这会可以从缓存中拿到半成品的 A
    对象,那么这个时候 B 对象的属性就填充完了。
  • 最后返回来继续完成 A 对象的属性填充,把实例化后并填充了属性的 B 对象赋值
    给 A 对象的 b 属性,这样就完成了一个循环依赖操作。
    在这里插入图片描述
    在这里插入图片描述
  • 从测试效果和截图依赖过程中可以看到,一级缓存也可以解决简单场景的循环依赖
    问题。
  • 其实 getBean,是整个解决循环依赖的核心内容,A 创建后填充属性时依赖 B,
    那么就去创建 B,在创建 B 开始填充时发现依赖于 A,但此时 A 这个半成品对
    象已经存放在缓存到 singletonObjects 中了,所以 B 可以正常创建,在通
    过递归把 A 也创建完整了。

有了以上这部分关于循环依赖的处理内容,在理解循环依赖就没那么复杂了。接下来我们带着这个感觉去思考如果有对象不只是简单的对象,还有代理对
象,还有AOP应用,要怎么处理这样的依赖问题。整体设计结构如下图:

在这里插入图片描述

  • 关于循环依赖在我们目前的 Spring 框架中扩展起来也并不会太复杂,主要就是对
    于创建对象的提前暴露,如果是工厂对象则会使用 getEarlyBeanReference 逻辑
    提前将工厂🏭对象存放到三级缓存中。等到后续获取对象的时候实际拿到的是工厂
    对象中 getObject,这个才是最终的实际对象。

  • 在创建对象的 AbstractAutowireCapableBeanFactory#doCreateBean
    方法中,提前暴露对象以后,就可以通过接下来的流程,getSingleton 从三个缓存
    中以此寻找对象,一级、二级如果有则直接取走,如果对象是三级缓存中则会从三
    级缓存中获取后并删掉工厂对象,把实际对象放到二级缓存中。

  • 最后是关于单例的对象的注册操作,这个注册操作就是把真实的实际对象放到一级
    缓存中,因为此时它已经是一个成品对象了。

处理循环依赖核心流程的类关系

  • 循环依赖的核心功能实现主要包括 DefaultSingletonBeanRegistry 提供三级缓存:
    singletonObjects、earlySingletonObjects、
    singletonFactories,分别存放成品对象、半成品对象和工厂对象。同时包
    装三个缓存提供方法:getSingleton、registerSingleton、addSingletonFactory,这
    样使用方就可以分别在不同时间段存放和获取对应的对象了。
  • 在 AbstractAutowireCapableBeanFactory 的 doCreateBean 方法中,提供了关于
    提前暴露对象的操作,addSingletonFactory(beanName, () ->
    getEarlyBeanReference(beanName, beanDefinition,
    finalBean));,以及后续获取对象和注册对象的操作,exposedObject =
    getSingleton(beanName);、registerSingleton(beanName,
    exposedObject);,经过这样的处理就可以完成对复杂场景循环依赖的操作。
  • 另外在 DefaultAdvisorAutoProxyCreator 提供的切面服务中,也需要实现接口
    InstantiationAwareBeanPostProcessor 新增的 getEarlyBeanReference 方法,便于
    把依赖的切面对象也能存放到三级缓存中,处理对应的循环依赖。

3、设置三级缓存

 public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
   /**
 * Internal marker for a null singleton object:
 * used as marker value for concurrent Maps (which don't support null values).
 */
protected static final Object NULL_OBJECT = new Object();

// 一级缓存,普通对象
/**
 * Cache of singleton objects: bean name --> bean instance
 */
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

// 二级缓存,提前暴漏对象,没有完全实例化的对象
/**
 * Cache of early singleton objects: bean name --> bean instance
 */
protected final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();

// 三级缓存,存放代理对象
/**
 * Cache of singleton factories: bean name --> ObjectFactory
 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>();

private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

@Override
public Object getSingleton(String beanName) {
    Object singletonObject = singletonObjects.get(beanName);
    if (null == singletonObject) {
        singletonObject = earlySingletonObjects.get(beanName);
        // 判断二级缓存中是否有对象,这个对象就是代理对象,因为只有代理对象才会放到三级缓存中
        if (null == singletonObject) {
            ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                // 把三级缓存中的代理对象中的真实对象获取出来,放入二级缓存中
                earlySingletonObjects.put(beanName, singletonObject);
                singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

public void registerSingleton(String beanName, Object singletonObject) {
    singletonObjects.put(beanName, singletonObject);
    earlySingletonObjects.remove(beanName);
    singletonFactories.remove(beanName);
}

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
    if (!this.singletonObjects.containsKey(beanName)) {
        this.singletonFactories.put(beanName, singletonFactory);
        this.earlySingletonObjects.remove(beanName);
    }
}

public void registerDisposableBean(String beanName, DisposableBean bean) {
    disposableBeans.put(beanName, bean);
}

public void destroySingletons() {
    Set<String> keySet = this.disposableBeans.keySet();
    Object[] disposableBeanNames = keySet.toArray();

    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        Object beanName = disposableBeanNames[i];
        DisposableBean disposableBean = disposableBeans.remove(beanName);
        try {
            disposableBean.destroy();
        } catch (Exception e) {
            throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
        }
    }
}
}
  • 在用于提供单例对象注册的操作的 DefaultSingletonBeanRegistry 类中,共有三个
    缓存对象的属性;singletonObjects、earlySingletonObjects、singletonFactories,
    如它们的名字一样,用于存放不同类型的对象(单例对象、早期的半成品单例对
    象、单例工厂对象)。
  • 紧接着在这三个缓存对象下提供了获取、添加和注册不同对象的方法,包括:
    getSingleton、registerSingleton、addSingletonFactory,其实后面这两个方法都比
    较简单,主要是 getSingleton 的操作,它是在一层层处理不同时期的单例对象,
    直至拿到有效的对象。

3、提前暴露对象

   public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
    // 判断是否返回代理 Bean 对象
    Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
    if (null != bean) {
        return bean;
    }

    return doCreateBean(beanName, beanDefinition, args);
}

protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
    Object bean = null;
    try {
        // 实例化 Bean
        bean = createBeanInstance(beanDefinition, beanName, args);

        // 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来
        if (beanDefinition.isSingleton()) {
            Object finalBean = bean;
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
        }

        // 实例化后判断
        boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
        if (!continueWithPropertyPopulation) {
            return bean;
        }
        // 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值
        applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
        // 给 Bean 填充属性
        applyPropertyValues(beanName, bean, beanDefinition);
        // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
        bean = initializeBean(beanName, bean, beanDefinition);
    } catch (Exception e) {
        throw new BeansException("Instantiation of bean failed", e);
    }

    // 注册实现了 DisposableBean 接口的 Bean 对象
    registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

    // 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE
    Object exposedObject = bean;
    if (beanDefinition.isSingleton()) {
        // 获取代理对象
        exposedObject = getSingleton(beanName);
        registerSingleton(beanName, exposedObject);
    }
    return exposedObject;

}

protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
    Object exposedObject = bean;
    for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
            exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(exposedObject, beanName);
            if (null == exposedObject) return exposedObject;
        }
    }

    return exposedObject;
}
  • 在 AbstractAutowireCapableBeanFactory#doCreateBean 的方法中主要是扩展了对
    象的提前暴露 addSingletonFactory 了,和单例对象的获取
    getSingleton 以及注册操作 registerSingleton。
  • 这里提到一点 getEarlyBeanReference 就是定义在如 AOP 切面中这样的代理对
    象,可以参考源码中接口InstantiationAwareBeanPostProcessor#getEarlyBeanReference 方法的实现。

测试

  1. 事先准备
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 属性配置文件-spring.xml
    在这里插入图片描述
  • 这个里的配置就很简单了,配置 husband 依赖 wife,配置 wife 依赖 husband 和
    mother,最后是关于 AOP 切面的依赖使用
  1. 单元测试
    在这里插入图片描述
  2. 测试结果
    在这里插入图片描述
  • 从测试结果可以看到,无论是简单对象依赖 老公依赖媳妇、媳妇依赖老公,还是
    代理工程对象或者 AOP 切面对象都可以在三级缓存下解决循环依赖的问题了。
  • 此外从运行截图 DefaultSingletonBeanRegistry#getSingleton 中也
    可以看到凡事需要三级缓存存放工厂对象的类,都会使用到 getObject 获取真实
    对象,并随后存入半成品对象 earlySingletonObjects 中以及移除工厂对象。

结论

  • Spring 中所有的功能都是以解决 Java 编程中的特性而存在的,如果没有 Spring 框架的情况下,
    可能我们也会尽可能避免写出循环依赖的操作,因为在没有经过加工处理后,这样的依赖关系肯定会报错的。那么这也就是程序从能用到好用的升级
  • 在解决循环依赖的核心流程中,主要是提前暴露对象的设计,以及建立三级缓存的数据结构来存放不同时期的对象,如果说没有如切面和工厂中的代理对象,那么二级缓存也就可以解决了,哪怕是只有一级缓存。但为了设计上的合理和可扩展性,所以创建了三级缓存来放置不同时期的对象。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值