循环依赖:Spring的三级缓存恋爱指南

4.2 循环依赖:Spring的三级缓存恋爱指南

引言

想象一下,A对象依赖B对象,B对象又依赖A对象,这种"你中有我,我中有你"的关系,像极了恋爱中的情侣。Spring是如何处理这种"循环依赖"的呢?今天我们就来揭秘Spring的三级缓存恋爱指南!


1. 循环依赖的"恋爱困境"

场景描述:

  • A对象需要B对象才能完成初始化。
  • B对象也需要A对象才能完成初始化。
  • 结果:A和B陷入了"你等我,我等你"的死循环。

Spring的解决方案:三级缓存机制。


2. 三级缓存机制详解

Spring通过三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)来解决循环依赖问题。我们可以把这三层缓存比作恋爱的三个阶段:

缓存层级 比喻 作用
singletonObjects 正式结婚 存放完全初始化好的Bean,可以直接使用。
earlySingletonObjects 订婚 存放提前暴露的Bean(半成品),用于解决循环依赖。
singletonFactories 相亲 存放Bean的工厂对象,用于创建Bean的早期引用(代理对象或原始对象)。

3. 代码实战:循环依赖的完整示例

以下是一个完整的Spring Boot项目示例,展示循环依赖的处理过程。

项目结构
css 复制代码
src/main/java/com/example/demo/
    ├── DemoApplication.java
    ├── config/
    │   └── AppConfig.java
    ├── service/
    │   ├── ServiceA.java
    │   └── ServiceB.java
代码实现

ServiceA.java

java 复制代码
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {
    private final ServiceB serviceB;

    @Autowired
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
        System.out.println("ServiceA initialized!");
    }

    public void sayHello() {
        System.out.println("Hello from ServiceA!");
    }
}

ServiceB.java

java 复制代码
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceB {
    private final ServiceA serviceA;

    @Autowired
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
        System.out.println("ServiceB initialized!");
    }

    public void sayHello() {
        System.out.println("Hello from ServiceB!");
    }
}

AppConfig.java

java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.example.demo.service")
public class AppConfig {
}

DemoApplication.java

java 复制代码
package com.example.demo;

import com.example.demo.service.ServiceA;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        ServiceA serviceA = context.getBean(ServiceA.class);
        serviceA.sayHello();
    }
}
运行结果
csharp 复制代码
ServiceA initialized!
ServiceB initialized!
Hello from ServiceA!

4. 三级缓存的工作流程

  1. 创建A对象:

    • Spring调用ServiceA的构造函数,发现需要注入ServiceB。
    • 将A对象的工厂(singletonFactories)放入缓存。
  2. 创建B对象:

    • Spring调用ServiceB的构造函数,发现需要注入ServiceA。
    • 从singletonFactories中获取A对象的早期引用(半成品),并放入earlySingletonObjects。
  3. 完成A对象的初始化:

    • 将B对象注入A对象。
    • 将A对象放入singletonObjects,完成初始化。
  4. 完成B对象的初始化:

    • 将A对象注入B对象。
    • 将B对象放入singletonObjects,完成初始化。

5. 图文解析

以下是一个简单的流程图,展示三级缓存的工作过程:

css 复制代码
+-------------------+       +-------------------+       +-------------------+
| singletonFactories | ----> | earlySingletonObjects | ----> | singletonObjects |
+-------------------+       +-------------------+       +-------------------+
        |                           |                           |
        | 1. 创建A对象               | 2. 创建B对象               | 3. 完成初始化
        | 放入工厂缓存               | 获取A的早期引用            | 放入正式缓存
        v                           v                           v

6. 常见面试题

  1. Spring如何解决循环依赖?

    • 通过三级缓存机制,提前暴露Bean的早期引用。
  2. 三级缓存分别是什么?

    • singletonObjects:存放完全初始化的Bean。
    • earlySingletonObjects:存放提前暴露的Bean(半成品)。
    • singletonFactories:存放Bean的工厂对象。
  3. 循环依赖的局限性是什么?

    • 只支持单例Bean的循环依赖。
    • 不支持原型(Prototype)Bean的循环依赖。

7. 总结

Spring的三级缓存机制就像一场精心策划的恋爱:

  • 相亲阶段 :通过singletonFactories找到合适的对象。
  • 订婚阶段 :通过earlySingletonObjects确定关系。
  • 结婚阶段 :通过singletonObjects完成最终绑定。

通过这种方式,Spring成功解决了循环依赖的"恋爱困境",让Bean们能够和谐共存!


彩蛋:如果你在面试中被问到循环依赖,可以幽默地说:"Spring的三级缓存就像恋爱中的三个阶段,从相亲到结婚,每一步都不能少!"

相关推荐
每天进步一点点km1 小时前
自用-牛客网-华为机试编程模拟题1-python
华为od·华为·面试
frjc7 小时前
分布式服务拆分实战指南:从底层逻辑到面试话术
微服务·面试·职场和发展
考研保研资料分享7 小时前
2025生科保研北大直博:英语摘要、科研PPT与赶场复盘
面试
香蕉大香蕉(06敬畏市场)7 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习
ShineWinsu7 小时前
对于Qt:QCheckBox常见属性的详细解析
c++·qt·面试·笔试·gui·qcheckbox·多选框
师夷长技以自愈8 小时前
第 3 讲:多 Agent——Supervisor 分诊 + 专职 Worker
面试
怕浪猫8 小时前
我有个朋友,工作八年,公司从 300 人减到 80 人
面试·程序员
考研保研资料分享8 小时前
2024财会跨保北大金融科技:三场笔面试与经管法混申复盘
科技·面试·金融
H_BB9 小时前
面试笔记:数据加密和心跳机制
笔记·面试·职场和发展
j7~10 小时前
【C++ 面试笔记】(篇二):类与数据抽象 & STL 容器与算法
开发语言·c++·面试·stl容器和算法·类和数据抽象