循环依赖: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的三级缓存就像恋爱中的三个阶段,从相亲到结婚,每一步都不能少!"

相关推荐
Interview Aid1121 小时前
Amazon SDE Interview Process:OA、VO 与面试流程详解
面试·职场和发展
沙漠之主4 小时前
计算机二级考试备考全攻略:从报名到通关
计算机网络·面试
燐妤4 小时前
LangGraph-复习总览
python·ai·面试·agent·学习方法·langgraph
ShineWinsu6 小时前
对于Redis:Hash类型的解析
c++·redis·分布式·缓存·面试·hash·哈希表
一只旭宝8 小时前
C++ 后端开发面试题整理
java·c++·面试
ShineWinsu10 小时前
对于Redis:List类型的解析
数据库·c++·redis·链表·缓存·面试·list
liangshanbo121510 小时前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
江华森10 小时前
大模型应用技术-Prompt工程实践 — 学习笔记
面试
keke.shengfengpolang11 小时前
2027届大数据管理与应用求职:采购数字化岗位JD数据能力拆解
面试
怕浪猫1 天前
LLM 面试必问的 8 个问题,答不上来直接淘汰
人工智能·python·面试