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

相关推荐
江城开朗的豌豆17 分钟前
Proxy:JavaScript中的'变形金刚',让你的对象为所欲为!
前端·javascript·面试
江城开朗的豌豆25 分钟前
JavaScript中的instanceof:你的代码真的认识'自家孩子'吗?
前端·javascript·面试
蒟蒻小袁29 分钟前
力扣面试150题--课程表
算法·leetcode·面试
张风捷特烈43 分钟前
每日一题 Flutter#7,8 | 关于 State 两道简答题
android·flutter·面试
乄夜10 小时前
嵌入式面试高频(5)!!!C++语言(嵌入式八股文,嵌入式面经)
c语言·c++·单片机·嵌入式硬件·物联网·面试·职场和发展
拉不动的猪12 小时前
安卓和ios小程序开发中的兼容性问题举例
前端·javascript·面试
wandongle14 小时前
HTML面试整理
前端·面试·html
liang_jy15 小时前
观察者模式
设计模式·面试
JiangJiang17 小时前
🔥 面试官:Webpack 为什么能热更新?你真讲得清吗?
前端·面试·webpack
蒟蒻小袁17 小时前
力扣面试150题--被围绕的区域
leetcode·面试·深度优先