notes/Spring三级缓存机制学习笔记.md
2025-06-25 19:21:02 +08:00

8.6 KiB
Raw Blame History

Spring三级缓存机制学习笔记

📚 概述

Spring三级缓存是Spring框架用来解决单例Bean循环依赖问题的核心机制。它通过三个不同的缓存Map来存储Bean在不同生命周期阶段的状态。


🎯 学习目标

  • 理解什么是循环依赖问题
  • 掌握三级缓存的结构和作用
  • 理解循环依赖的解决流程
  • 学会分析相关源码

🔍 核心概念

什么是循环依赖?

当两个或多个Bean相互依赖时形成的依赖环

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB; // A依赖B
}

@Component  
public class ServiceB {
    @Autowired
    private ServiceA serviceA; // B依赖A
}

问题:如果没有特殊处理,会陷入无限循环创建对象的死循环。


🏗️ 三级缓存结构

Spring使用三个Map来管理Bean的不同状态

/** 一级缓存完整的单例Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存早期暴露的Bean对象 */  
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 三级缓存Bean工厂对象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

各级缓存详解

缓存级别 名称 作用 存储内容
一级缓存 singletonObjects 成品仓库 完全初始化完成的Bean
二级缓存 earlySingletonObjects 半成品仓库 实例化但未完成依赖注入的Bean
三级缓存 singletonFactories 工厂仓库 Bean的ObjectFactory支持AOP代理

🔄 循环依赖解决流程

以ServiceA ↔ ServiceB循环依赖为例

流程图

1. 创建ServiceA
   ↓
2. ServiceA实例化 → 放入三级缓存
   ↓
3. ServiceA需要ServiceB → 开始创建ServiceB
   ↓
4. ServiceB实例化 → 放入三级缓存
   ↓
5. ServiceB需要ServiceA → 从缓存获取ServiceA早期引用
   ↓
6. ServiceA从三级缓存移至二级缓存
   ↓
7. ServiceB完成创建 → 放入一级缓存
   ↓
8. ServiceA完成创建 → 从二级缓存移至一级缓存

详细步骤

第1步开始创建ServiceA

// 1. 实例化ServiceA构造函数
ServiceA serviceA = new ServiceA();

// 2. 将ServiceA的工厂放入三级缓存
singletonFactories.put("serviceA", () -> getEarlyBeanReference("serviceA", serviceA));

// 3. 开始属性注入发现需要ServiceB

第2步开始创建ServiceB

// 1. 实例化ServiceB构造函数
ServiceB serviceB = new ServiceB();

// 2. 将ServiceB的工厂放入三级缓存
singletonFactories.put("serviceB", () -> getEarlyBeanReference("serviceB", serviceB));

// 3. 开始属性注入发现需要ServiceA

第3步获取ServiceA早期引用

// 调用getSingleton("serviceA", true)
Object serviceA = singletonObjects.get("serviceA");        // 一级缓存null
if (serviceA == null) {
    serviceA = earlySingletonObjects.get("serviceA");      // 二级缓存null
    if (serviceA == null) {
        ObjectFactory factory = singletonFactories.get("serviceA"); // 三级缓存:找到!
        serviceA = factory.getObject();                    // 获取早期引用
        earlySingletonObjects.put("serviceA", serviceA);   // 移到二级缓存
        singletonFactories.remove("serviceA");             // 从三级缓存移除
    }
}

第4步完成ServiceB创建

// ServiceB获得ServiceA的早期引用完成属性注入
serviceB.setServiceA(serviceA);

// ServiceB初始化完成放入一级缓存
singletonObjects.put("serviceB", serviceB);

第5步完成ServiceA创建

// ServiceA获得ServiceB的完整引用完成属性注入
serviceA.setServiceB(serviceB);

// ServiceA初始化完成从二级缓存移到一级缓存
singletonObjects.put("serviceA", serviceA);
earlySingletonObjects.remove("serviceA");

💡 核心源码分析

getSingleton方法核心逻辑

/**
 * 从缓存中获取单例Bean
 * @param beanName Bean名称
 * @param allowEarlyReference 是否允许早期引用
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 一级缓存获取完整Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    
    // 如果一级缓存没有且Bean正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 二级缓存获取早期Bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            
            // 如果二级缓存也没有,且允许早期引用
            if (singletonObject == null && allowEarlyReference) {
                // 三级缓存获取Bean工厂
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过工厂创建早期引用
                    singletonObject = singletonFactory.getObject();
                    // 升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 从三级缓存移除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

addSingletonFactory方法

/**
 * 添加单例工厂到三级缓存
 */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            // 从二级缓存移除
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

🤔 常见问题

Q1: 为什么需要三级缓存?二级不够吗?

答: 三级缓存主要是为了支持AOP代理

  • 如果Bean需要被代理如@Transactional不能直接暴露原始对象
  • 通过ObjectFactory可以在需要时创建代理对象
  • 保证循环依赖中获取的是正确的代理对象

Q2: 构造器循环依赖能解决吗?

答: 不能!三级缓存只能解决属性注入的循环依赖:

// ❌ 构造器循环依赖 - 无法解决
@Component
public class ServiceA {
    public ServiceA(ServiceB serviceB) { }
}

// ✅ 属性注入循环依赖 - 可以解决  
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

Q3: Prototype作用域的循环依赖呢

答: 不能解决!因为:

  • Prototype每次都创建新实例
  • 没有缓存机制
  • 会导致无限递归创建

🎯 实践建议

1. 避免循环依赖的最佳实践

// ❌ 避免:直接的双向依赖
@Service
public class UserService {
    @Autowired private OrderService orderService;
}

@Service  
public class OrderService {
    @Autowired private UserService userService;
}

// ✅ 推荐:引入第三方服务
@Service
public class UserOrderService {
    @Autowired private UserService userService;
    @Autowired private OrderService orderService;
    
    public void processUserOrder(Long userId, Long orderId) {
        // 协调两个服务的交互
    }
}

2. 使用@Lazy注解延迟加载

@Service
public class ServiceA {
    @Autowired
    @Lazy  // 延迟加载,避免循环依赖
    private ServiceB serviceB;
}

3. 通过ApplicationContext获取Bean

@Service
public class ServiceA implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    public void someMethod() {
        // 运行时获取,避免循环依赖
        ServiceB serviceB = applicationContext.getBean(ServiceB.class);
    }
}

📝 学习总结

关键点记忆

  1. 三级缓存目的解决单例Bean的循环依赖
  2. 核心思想提前暴露Bean的早期引用
  3. 适用范围:仅限于属性注入的循环依赖
  4. AOP支持通过ObjectFactory支持代理对象

面试重点

  • 能够清晰描述三级缓存的结构和作用
  • 能够完整说出循环依赖的解决流程
  • 了解哪些情况下循环依赖无法解决
  • 知道避免循环依赖的最佳实践

📚 扩展阅读

  • Spring官方文档Bean的生命周期
  • 源码位置:DefaultSingletonBeanRegistry
  • 相关概念Spring AOP、Bean作用域
  • 设计模式:工厂模式、单例模式

创建时间2025-06-25
最后更新2025-06-25
难度等级