Spring 中 Bean 的循环依赖

目录

  • 1、什么是循环依赖
  • 2、Spring 中循环依赖类型
        • 2.1 构造器循环依赖
        • 2.2 prototype模式循环依赖
        • 2.2 singleton模式的 setter 循环依赖
  • 3、结论
  • 4、循环依赖问题解决

1、什么是循环依赖

循环依赖即循环引用,就是两个或多个 bean 互相之间持有对方,比如 CircleA 引用 CircleB,CircleB 引用 CircleA 而形成一个环。
区分:循环调用(方法之间的调用,无总结终结条件则会出现死循环,导致内存溢出)

2、Spring 中循环依赖类型

Spring 容器循环依赖包括:

  • 构造器循环依赖
  • prototype模式循环依赖
  • singleton模式的 setter 循环依赖

2.1 构造器循环依赖

@Component
public class CircleA {

    private CircleB circleB;

    public CircleA(CircleB circleB){
        this.circleB = circleB;
    }
}

@Component
public class CircleB {

    private CircleA circleA;

    public CircleB(CircleA circleA){
        this.circleA = circleA;
    }
}

过程分析

  • Spring 容器创建 “CircleA” bean,首先会去 “当前创建bean池” 查找是否当前bean正在创建,如果没发现,则继续准备其需要的构造器参数 “circleB”,并将 “circleA” 标识符放在 “当前创建bean池”
  • Spring 容器创建 “CircleB” bean,首先会去 “当前创建bean池” 查找是否当前bean正在创建,如果没发现,则继续准备其需要的构造器参数 “circleA”,但是发现此时 “circleA” 标识符在 “当前创建bean池” 中,所以出现循环引用,抛出异常

结果:应用正常启动失败,抛出异常BeanCurrentlyInCreationException
根本原因:Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器是用来完成实例化的,所以构造器的循环依赖无法解决

2.2 prototype模式循环依赖

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CircleA {

    @Autowired
    private CircleB circleB;
    
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CircleB {

    @Autowired
    private CircleA circleA;

}

结果:应用正常启动不出现报错,非单例Bean默认不会初始化,而是在使用的时候进行初识化,所以在单例bean中@Autowired一下CircleA或者CircleB,然后启动就会报错

2.2 singleton模式的 setter 循环依赖

@Component
public class CircleA {

    private CircleB circleB;

    public CircleB getCircleB() {
        return circleB;
    }

    public void setCircleB(CircleB circleB) {
        this.circleB = circleB;
    }
}

@Component
public class CircleB {

    private CircleA circleA;

    public CircleA getCircleA() {
        return circleA;
    }

    public void setCircleA(CircleA circleA) {
        this.circleA = circleA;
    }
}

结果:一切正产启动并使用。

3、结论

spring 可以解决 singleton模式属性 setter 方法注入循环依赖,但无法解决构造器注入循环依赖、prototype模式属性注入循环依赖问题

4、循环依赖问题解决

Spring容器使用三级缓存,提前暴露刚完成构造器注入但未完成其他步骤的bean来完成

对象加载形成循环原因过程如图所示:
Spring 中 Bean 的循环依赖_第1张图片
三级缓存解决循环依赖过程

# 一级缓存,存放完全实例化且属性赋值完成的 Bean ,可以直接使用
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 

# 二级缓存,存放早期 Bean 的引用,尚未装配属性的 Bean
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); 

# 三级缓存,存放实例化完成的 Bean 工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); 

Spring 中 Bean 的循环依赖_第2张图片

你可能感兴趣的:(spring,spring,java,原型模式)