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

相关推荐
敲敲了个代码2 小时前
从零实现一个「就地编辑」组件:深入理解 OOP 封装与复用的艺术
前端·javascript·学习·面试·前端框架
王中阳Go3 小时前
Go后端 vs Go AI应用开发重点关注什么?怎么学?怎么面试?
人工智能·面试·golang
程序员小胖3 小时前
每天一道面试题之架构篇|异步确保型事务——消息队列驱动的分布式事务解决方案
分布式·面试
是你的小橘呀3 小时前
像前任一样捉摸不定的异步逻辑,一文让你彻底看透——JS 事件循环
前端·javascript·面试
wjm0410064 小时前
秋招ios面试 -- 真题篇(三)
ios·面试·职场和发展
Baihai_IDP4 小时前
用户体验与商业化的两难:Chatbots 的广告承载困境分析
人工智能·面试·llm
灵犀坠4 小时前
前端高频知识点汇总:从手写实现到工程化实践(面试&开发双视角)
开发语言·前端·javascript·tcp/ip·http·面试·职场和发展
努力学算法的蒟蒻5 小时前
day24(12.4)——leetcode面试经典150
面试·职场和发展
TL滕5 小时前
从0开始学算法——第七天(快速排序算法)【面试高频】
笔记·学习·算法·面试·职场和发展·排序算法