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

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

引言

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


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

场景描述

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

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


2. 三级缓存机制详解

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

缓存层级 比喻 作用
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的三级缓存就像恋爱中的三个阶段,从相亲到结婚,每一步都不能少!"

相关推荐
Ruihong10 小时前
Vue withDefaults 转 React:VuReact 怎么处理?
vue.js·react.js·面试
kyriewen11 小时前
别再这样写 async/await 了:我在 Code Review 中见过最多的 8 个错误
前端·javascript·面试
烬羽16 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
云技纵横17 小时前
一个 @Async,把 @Transactional 的事务边界打穿了
后端·面试
想要成为糕糕手17 小时前
Harness Engineering:大模型时代的“马鞍”——从记忆层开始,让AI真正为你所用
面试·ai编程·claude
kyriewen1 天前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
她的男孩1 天前
后台接口加密别只会 HTTPS,ForgeAdmin 的 RSA + SM4/AES 源码拆解
后端·面试·开源
Randyliu1 天前
20260508-Agent搭建记录以及对ReAct框架的理解
面试·agent
ZzT1 天前
公司用 AI 筛简历,他写了个 AI 帮你挑公司
面试·aigc·ai编程
PBitW1 天前
GPT训练我的第四天,被打惨了!!!😭😭😭
前端·javascript·面试