Spring-bean的循环依赖

103 阅读2分钟

一、循环依赖(循环引用)的概念

循环依赖其实就是循环引用,也就是一个或多个Bean互相持有对方,最终形成闭环。比如:A依赖于B,B依赖与A。

@Component
public class A{
    @Autowired
    private B b;
}
@Component
public class B{
    @Autowired
    private A a;
}

以下都属于循环引用

1691328499001.jpg

二、三级缓存解决循环依赖

循环依赖会带来死循环的问题。为了解决这个问题,Spring中引入了三级缓存。对应的三级缓存如下所示:

//单实例对象注册器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry{
    private static final int SUPPERSSED_EXCEPTIONS_LIMIT=100;
    //一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    //二级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
    //三级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
缓存名称源码名称作用
一级缓存singletonObjects单例池,缓存已经经历了完整的生命周期并且已经初始化完成的Bean对象。限制Bean在BeanFactory中只存一份,即实现singleton scope,解决不了循环依赖。
二级缓存earlySingletonObjects缓存早期的Bean对象(生命周期还没有走完)。
三级缓存singletonFactories缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的。可以创建代理对象,也可以创建普通对象。

2.1 二级缓存

如果要想打破循环依赖,就需要一个中间人的参与,这个人就是二级缓存。

a26f30044432e15e936fa666c783d86.png

如果只是普通对象间的相互依赖,只用二级缓存就可以。但是如果涉及到代理对象,必须要引入三级缓存。

2.2 三级缓存

三级缓存解决的是Bean对象初始化过程中的循环依赖问题。

1691331410743.jpg

三级缓存解决了Spring框架大部分的循环依赖问题。但是仍然还有不能解决的循环依赖问题,比如在构造方法注入产生了循环依赖。

2.3 构造方法出现循环依赖

如下面代码所示,A对象与B对象都在各自的构造方法中注入对方。

@Component
public class A{
    //成员变量B
    private B b;
    
    A(B b){
        this.b = b;
    }
}
@Component
public class B{
    //成员变量A
    private A a;
    
    B(A a){
        this.a = a;
    }
}

三级缓存可以解决初始化过程中的循环依赖,不能解决构造函数产生的循环依赖。 解决方法:使用@Lazy注解,延迟加载注入对象。

@Component
public class A{
    //成员变量B
    private B b;
    
    A(@Lazy B b){
        this.b = b;
    }
}