spring三级缓存解决循环依赖

本文详细解释了循环依赖的概念,展示了在Java代码中的示例,并阐述了Spring框架如何通过三级缓存(singletonObjects,earlySingletonObjects,singletonFactories)来解决对象之间的循环依赖问题,特别是在涉及AOP代理对象时的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是循环依赖

在这里插入图片描述
上图就是循环依赖的三种情况,虽然方式不同,但是循环依赖的本质是一样的,就A的完整创建要依赖与B,B的完整创建要依赖于A,相互依赖导致没办法完整创建造成失败.
简单说循环依赖就是对象之间的循环引用,即两个或两个以上的Bean互相持有对方,最终形成闭环。

循环依赖代码演示

public class ClassA {

	private ClassB classB;

	public ClassB getClassB() {
		return classB;
	}

	public void setClassB(ClassB classB) {
		this.classB = classB;
	}
}

public class ClassB {

	private ClassA classA;

	public ClassA getClassA() {
		return classA;
	}

	public void setClassA(ClassA classA) {
		this.classA = classA;
	}
}

Spring如何解决循环依赖问题?

上述代码中对象的生命周期就两个:创建对象和属性填充,而Spring涉及到对象生命周期的方法就很多了,简单举例,如下图:
在这里插入图片描述
基于对上述代码的了解,我们知道肯定需要在调用构造方法创建完成后再暴露对象,再Spring中提供了三级缓存来处理这个事情,如下图:
在这里插入图片描述
对应到源码中具体处理循环依赖的流程如下:
在这里插入图片描述
上面就是Spring的生命周期方法和循环依赖出现相关的流程了.下面就是放入三级缓存的源码:

Spring正是通过singleton单例bean的setter方式解决循环依赖的,ClassA在创建的时候,会将自己放入到singletonObjectFactories三级缓存中,在属性装配的时候,检测到ClassA对象依赖于ClassB对象,Spring首先尝试从容器中获取ClassB,此时ClassB不存在,Spring容器会初始化ClassB,同时也将ClassB暴露到容器中,接着就是对ClassB进行属性装配,Spring从容器中获取ClassA,因为之前已经将ClassA暴露到容器中了,因此可以获取到ClassA实例,并将ClassA升级放入二级缓存earlySingletonObjects中,ClassA通过给Spring容器获取到ClassB,完成了ClassB的初始化操作,这样ClassA和ClassB都完成了对象初始化操作,便解决了循环依赖问题。

看看三级缓存的源码

/**
     * 添加对象到三级缓存
     *
     * @param beanName
     * @param singletonFactory
     */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    // 确保singletonFactory不为null
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    // 使用singletonObjects进行加锁,保证线程安全
    synchronized (this.singletonObjects) {
        //如果singletonObjects缓存中没有该对象
        if (!this.singletonObjects.containsKey(beanName)) {
            // 将对象放置到singletonFactories(三级缓存)中
            this.singletonFactories.put(beanName, singletonFactory);
            // 从earlySingletonObjects(二级缓存)中移除该对象
            this.earlySingletonObjects.remove(beanName);
            // 将beanName添加到已经注册的单例集中
            this.registeredSingletons.add(beanName);
        }
    }
}

放入二级缓存的源码:

/**
     * 返回在给定名称下注册的(原始)单例对象.检查已经实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)
     *
     * @param beanName
     * @param allowEarlyReference
     * @return
     */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 不需要完全获取单例锁的情况下快速检查现有实例
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果单例对象为空,并且当前单例正在创建中,则尝试获取早期单例对象
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 如果早期单例对象为空,并且允许早期引用,则再完全获取单力所的情况下创建早期单例对象
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // 检查早期单例对象是否存在
                singletonObject = this.singletonObjects.get(beanName);
                // 如果早期对象仍然为空则创建单例对象
                if (singletonObject == null) {
                    // 从二级缓存获取
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 获取不到对象从三级缓存中获取
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            // 获取到添加到二级缓存并从三级缓存中移除该对象
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

放入一级缓存中的源码:

/**
     * 将单例对象添加到一级缓存
     *
     * @param beanName
     * @param singletonObject
     */
protected void addSingleton(String beanName, Object singletonObject) {
    // 使用singletonObjects进行加锁,保证线程安全
    synchronized (this.singletonObjects) {
        // 将映射关系添加到一级缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 从三级缓存;二级缓存中移除该对象
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        // 将beanName添加到已经注册的单例集中
        this.registeredSingletons.add(beanName);
    }
}

总结

三级缓存分别有什么作用
  1. singletonObjects:缓存经过了完整生命周期的bean
  2. earlySingletonObjects:缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,如果这个bean要经过AOP,那么就会把代理对象放入到earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects,但是不管怎么样就是代理对象,代理对象所代理的原始对象也是没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean
  3. singletonFactories:缓存的是一个ObjectFactory,也就是一个Lambda表达式,在每个bean的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到, 如果当前bean没有出现循环依赖,那么这个Lambda表达式就没有用,当前bean按照自己的生命周期正常执行,执行完直接把当前bean放入singletonObjects中,如果当前bean在依赖注入时出现了循环依赖,则从三级缓存中拿到Lambda表达式,并执行Lambda表达式得到一个对象,并把得到的对象放入到二级缓存(如果当前bean需要AOP,那么执行Lambda表达式,得到的就是对应的代理对象,如果无需AOP,则直接得到一个原始对象)
  4. 其实还要一个缓存,用来记录某个原始对象是否进行过AOP了
为什么需要三级缓存

如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时就会出现,对于A而言,它的bean对象应该是AOP之后的代理对象,而B的a属性对应的不是AOP之后的代理对象,这就产生了冲突,B依赖的A和最终的A不是同一个对象,三级缓存主要处理的是AOP的代理对象,存储的是一个ObjectFactory

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值