Spring 如何解决循环依赖 笔记251008

Spring 如何解决循环依赖 笔记251008

一句话总结:
Spring 只解决「单例 Bean 的字段 / setter 循环依赖」,靠的是三级缓存 + 提前暴露"半成品"引用;构造器注入、原型作用域的循环依赖直接抛异常。

下面把完整机制拆开讲清:


  1. 能解 vs 不能解的场景

依赖方式 作用域 Spring 能否解决 原因
字段 / setter 注入 singleton 三级缓存可提前暴露对象引用
构造器注入 singleton 实例化阶段就卡住,无法提前暴露
任何方式 prototype 原型 Bean 不缓存,每次新建,无法复用引用

  1. 三级缓存到底做了什么

位置:DefaultSingletonBeanRegistry

缓存 变量 内容 生命周期阶段
一级 singletonObjects ConcurrentHashMap 完全初始化好的单例 就绪可用
二级 earlySingletonObjects ConcurrentHashMap 提前曝光的"裸"对象(可能代理) 已实例化,未填充属性
三级 singletonFactories HashMap<String, ObjectFactory<?>> 能生成早期引用的 ObjectFactory 实例化后马上放进去

  1. 字段注入循环依赖的完整时间线

以「A → B,B → A」字段注入为例:

  1. getBean(A)

    • 实例化 A(构造器完成)→ 创建 ObjectFactory 并放入三级缓存
    • 开始填充 A 的字段,发现需要 B → getBean(B)
  2. getBean(B)

    • 实例化 B → 也把自己的 ObjectFactory 放入三级缓存
    • 填充 B 的字段,发现需要 A → 又去 getBean(A)
  3. 此时 A 还在三级缓存,调用 ObjectFactory.getObject() 拿到早期引用(若需要 AOP 则在这里生成代理),把结果放进二级缓存 并删除三级缓存条目;B 拿到这个引用完成属性填充,初始化结束 → 放入一级缓存

  4. B 返回后,A 继续完成属性填充 → 初始化结束 → 放入一级缓存

整个过程没有两次初始化,也不会出现不完整对象被外部拿到


  1. 构造器注入为什么不行

构造器注入时,实例化阶段就必须把依赖传进去;

此时 Spring 还没机会把"半成品"放进任何缓存,

于是出现「A 等 B,B 等 A」的死锁,直接抛:

复制代码
BeanCurrentlyInCreationException

  1. 原型 Bean 为什么不行

原型 Bean 每次 getBean 都新建对象,不缓存;

无法复用同一个引用,因此无法通过"提前暴露"打破循环。


  1. 一句话记忆

单例 + 字段 / setter + 三级缓存 = 可解;
构造器 or 原型 = 无解。


  1. 开发建议

  • 尽量用「字段 / setter 注入」或「@Autowired + @Lazy」延迟加载,避免构造器循环依赖
  • 设计层面消除循环:抽离公共接口、事件驱动、第三方持有双方引用等




Spring通过三级缓存机制来解决单例Bean的循环依赖问题。下面详细讲解其工作原理。

核心原理:三级缓存

Spring在DefaultSingletonBeanRegistry类中维护了三个缓存级别:

java 复制代码
// 第一级缓存:存放完全初始化好的Bean(成品)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 第二级缓存:存放早期的Bean引用(半成品)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// 第三级缓存:存放Bean工厂,用于创建早期引用
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

解决循环依赖的完整流程

以A依赖B,B依赖A为例:

步骤1:创建Bean A

java 复制代码
// 1. 开始创建A,标记A为"创建中"
beforeSingletonCreation("A");

// 2. 实例化A(调用构造函数),此时A对象已存在但属性为null
instance = createBeanInstance("A");

// 3. 将A的ObjectFactory放入三级缓存
addSingletonFactory("A", () -> getEarlyBeanReference("A", mbd, instance));

此时三级缓存中有了A的工厂,可以生产A的早期引用。

步骤2:填充A的属性,发现依赖B

java 复制代码
// 4. 填充A的属性
populateBean("A", mbd, instanceWrapper);
// 发现需要注入B,开始获取Bean B

步骤3:创建Bean B

java 复制代码
// 5. 开始创建B
beforeSingletonCreation("B");

// 6. 实例化B
instance = createBeanInstance("B");

// 7. 将B的ObjectFactory放入三级缓存
addSingletonFactory("B", () -> getEarlyBeanReference("B", mbd, instance));

步骤4:填充B的属性,发现依赖A(循环依赖发生!)

java 复制代码
// 8. 填充B的属性,发现需要注入A
// 开始获取Bean A的流程:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 第一级缓存查找(成品)
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 第二级缓存查找(半成品)
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // 第三级缓存查找(工厂)
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 调用工厂获取早期引用
                    singletonObject = singletonFactory.getObject();
                    // 将A从三级缓存升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

步骤5:完成B的创建

java 复制代码
// 9. 将A的早期引用注入到B中
// 10. 完成B的初始化
initializeBean("B", exposedObject, mbd);

// 11. 将B放入一级缓存
addSingleton("B", singletonObject);

步骤6:完成A的创建

java 复制代码
// 12. 回到A的创建流程,现在可以获取到完整的B
// 13. 将B注入到A中
// 14. 完成A的初始化
initializeBean("A", exposedObject, mbd);

// 15. 将A放入一级缓存
addSingleton("A", singletonObject);

关键方法解析

getEarlyBeanReference方法

这个方法负责处理AOP代理:

java 复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // 如果需要代理,这里会返回代理对象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

addSingleton方法

java 复制代码
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 放入一级缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 从二、三级缓存移除
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

流程图解

graph TD A[开始创建A] --> B[实例化A] B --> C[将A工厂放入三级缓存] C --> D[填充A属性发现依赖B] D --> E[开始创建B] E --> F[实例化B] F --> G[将B工厂放入三级缓存] G --> H[填充B属性发现依赖A] H --> I[从缓存获取A] I --> J{三级缓存查找} J --> K[找到A工厂] K --> L[创建A早期引用] L --> M[A放入二级缓存] M --> N[A注入到B] N --> O[完成B初始化] O --> P[B放入一级缓存] P --> Q[回到A创建流程] Q --> R[B注入到A] R --> S[完成A初始化] S --> T[A放入一级缓存]

三级缓存的分工

缓存级别 名称 内容 作用
一级缓存 singletonObjects 完整的Bean 提供最终可用的Bean
二级缓存 earlySingletonObjects 早期Bean引用 避免重复创建早期引用
三级缓存 singletonFactories Bean工厂 处理AOP代理,按需创建代理对象

适用场景与限制

支持的循环依赖

  • Setter注入
  • Field注入 (@Autowired) ✓
  • 单例Bean

不支持的循环依赖

  • 构造器注入
java 复制代码
@Component
public class A {
    private B b;
    @Autowired
    public A(B b) { // 构造器循环依赖无法解决
        this.b = b;
    }
}
  • 原型Bean (prototype) ✗
  • @Async注解的Bean

总结

Spring通过三级缓存机制优雅地解决了循环依赖问题:

  1. 提前暴露:在Bean实例化后立即暴露引用
  2. 工厂模式:通过ObjectFactory延迟处理AOP代理
  3. 缓存升级:从三级→二级→一级的渐进式完善
  4. 代理兼容:确保代理对象的一致性

这种设计既解决了循环依赖,又保持了Spring AOP的正常工作,是Spring框架中非常精妙的设计之一。





1️⃣ 参考 1

Spring 如何解决循环依赖

1. 循环依赖的类型

1.1 常见的循环依赖场景

java 复制代码
// 场景1:相互依赖
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

// 场景2:三组件循环依赖
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Component
public class ServiceB {
    @Autowired
    private ServiceC serviceC;
}

@Component
public class ServiceC {
    @Autowired
    private ServiceA serviceA;
}

// 场景3:自我依赖(少见但可能)
@Component
public class SelfDependent {
    @Autowired
    private SelfDependent self;
}

2. Spring 的解决方案

2.1 三级缓存机制

Spring 通过三级缓存 + 提前暴露引用的方式解决循环依赖:

java 复制代码
// Spring 内部的三级缓存结构
public class DefaultSingletonBeanRegistry {
    // 第一级缓存:完整Bean(已初始化完成)
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 第二级缓存:早期Bean(已实例化但未初始化)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 第三级缓存:Bean工厂(用于创建早期引用)
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    // 正在创建中的Bean
    private final Set<String> singletonsCurrentlyInCreation = 
        Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

2.2 详细解决流程

java 复制代码
// 以 A → B → A 为例的详细流程
@Component
public class A {
    @Autowired
    private B b;
    private String name = "A";
    
    public void init() {
        System.out.println("A initialized");
    }
}

@Component
public class B {
    @Autowired
    private A a;
    private String name = "B";
    
    public void init() {
        System.out.println("B initialized");
    }
}

解决步骤

java 复制代码
// 步骤1:开始创建A
1. 调用 getBean("A")
2. 标记 A 正在创建:singletonsCurrentlyInCreation.add("A")
3. 实例化 A 对象(此时A是原始对象,b=null,未调用init方法)

// 步骤2:提前暴露A的引用
4. 将A的ObjectFactory放入三级缓存:
   singletonFactories.put("A", () -> getEarlyBeanReference("A", mbd, aInstance))

// 步骤3:填充A的属性(发现依赖B)
5. 调用 populateBean("A"),发现需要注入B
6. 调用 getBean("B")

// 步骤4:开始创建B
7. 标记 B 正在创建:singletonsCurrentlyInCreation.add("B")
8. 实例化 B 对象
9. 将B的ObjectFactory放入三级缓存

// 步骤5:填充B的属性(发现依赖A)
10. 调用 populateBean("B"),发现需要注入A
11. 调用 getBean("A")

// 步骤6:获取A的早期引用
12. 发现A正在创建中
13. 从三级缓存获取A的ObjectFactory
14. 调用 factory.getObject() 获取A的早期引用
15. 将A从三级缓存移到二级缓存:earlySingletonObjects.put("A", aEarlyReference)
16. 返回A的早期引用给B

// 步骤7:完成B的创建
17. B持有A的早期引用,完成属性注入
18. 初始化B(调用@PostConstruct、init方法等)
19. 将B放入一级缓存,清理二三级缓存

// 步骤8:完成A的创建
20. A获得完整的B实例,完成属性注入
21. 初始化A
22. 将A放入一级缓存,清理二三级缓存

3. 源码实现解析

3.1 核心方法:doCreateBean

java 复制代码
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 1. 实例化Bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();
    
    // 2. 提前暴露引用(解决循环依赖的关键)
    if (earlySingletonExposure) {
        // 将Bean工厂添加到三级缓存
        addSingletonFactory(beanName, 
            () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    
    // 3. 属性注入(可能触发循环依赖)
    populateBean(beanName, mbd, instanceWrapper);
    
    // 4. 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    return exposedObject;
}

3.2 核心方法:getSingleton

java 复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存查找
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存查找
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 4. 创建早期引用
                    singletonObject = singletonFactory.getObject();
                    // 5. 移动到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

3.3 核心方法:addSingletonFactory

java 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            // 清理二级缓存(确保数据一致性)
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

4. AOP 代理的处理

4.1 代理对象的循环依赖

java 复制代码
@Component
public class TransactionalService {
    @Autowired
    private AnotherService anotherService;
    
    @Transactional
    public void doSomething() {
        // 事务方法
    }
}

@Component
public class AnotherService {
    @Autowired
    private TransactionalService transactionalService;  // 需要代理对象
}

Spring 的处理方式

java 复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // AbstractAutoProxyCreator 在这里创建代理对象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

5. 无法解决的循环依赖场景

5.1 构造器注入循环依赖

java 复制代码
@Component
public class ConstructorA {
    private final ConstructorB b;
    
    @Autowired
    public ConstructorA(ConstructorB b) {  // 构造器注入
        this.b = b;
    }
}

@Component
public class ConstructorB {
    private final ConstructorA a;
    
    @Autowired
    public ConstructorB(ConstructorA a) {  // 构造器注入
        this.a = a;
    }
}

错误信息

less 复制代码
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  constructorA defined in file [...]
↑     ↓
|  constructorB defined in file [...]
└─────┘

5.2 原型作用域的循环依赖

java 复制代码
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeA {
    @Autowired
    private PrototypeB b;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeB {
    @Autowired
    private PrototypeA a;
}

原因:原型 Bean 不缓存,每次都会创建新实例。

5.3 @Async 方法的循环依赖

java 复制代码
@Component
public class AsyncService {
    @Autowired
    private AnotherService anotherService;
    
    @Async
    public void asyncMethod() {
        // 异步方法
    }
}

@Component
public class AnotherService {
    @Autowired
    private AsyncService asyncService;  // 需要代理对象
}

6. 解决方案和最佳实践

6.1 避免循环依赖的设计

java 复制代码
// 方案1:使用接口分离
public interface IServiceA {
    void methodA();
}

public interface IServiceB {
    void methodB();
}

@Component
public class ServiceA implements IServiceA {
    @Autowired
    private IServiceB serviceB;
}

@Component  
public class ServiceB implements IServiceB {
    @Autowired
    private IServiceA serviceA;
}

// 方案2:使用 setter 注入替代字段注入
@Component
public class SetterA {
    private SetterB b;
    
    @Autowired
    public void setB(SetterB b) {
        this.b = b;
    }
}

// 方案3:使用 @Lazy 延迟加载
@Component
public class LazyA {
    @Lazy
    @Autowired
    private LazyB b;
}

6.2 应用启动时检测

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        
        // 开启循环依赖检测(默认就是true)
        app.setAllowCircularReferences(true);
        
        app.run(args);
    }
}

7. 调试和排查技巧

7.1 启用调试日志

properties 复制代码
 ##########  application.properties
logging.level.org.springframework.beans.factory=DEBUG
logging.level.org.springframework.context=DEBUG


 ##########  或者查看具体的循环依赖信息
spring.main.allow-circular-references=false  
 ##########  强制检测

7.2 使用 BeanPostProcessor 调试

java 复制代码
@Component
public class CircularDependencyDebugProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean initialized: " + beanName + " - " + bean.getClass().getSimpleName());
        return bean;
    }
}

8. 总结

Spring 通过三级缓存机制优雅地解决了循环依赖问题:

  • 支持场景:单例 Bean 的字段注入、setter 注入
  • 不支持场景:构造器注入、原型 Bean、某些代理场景
  • 🔧 解决方案:提前暴露引用 + 缓存不同状态的 Bean
  • 性能考虑:空间换时间,保证单例 Bean 的正确性

最佳实践

  1. 尽量避免循环依赖,优化设计
  2. 优先使用构造器注入(可以及早发现问题)
  3. 必要时使用 @Lazy 或 setter 注入
  4. 保持 Bean 的轻量,避免复杂的依赖关系




2️⃣ 参考 2

Spring 如何解决循环依赖

1. 循环依赖的类型

1.1 构造器循环依赖(无法解决)

java 复制代码
@Service
public class AService {
    private final BService bService;
    
    public AService(BService bService) {  // 构造器注入
        this.bService = bService;
    }
}

@Service
public class BService {
    private final AService aService;
    
    public BService(AService aService) {  // 构造器注入
        this.aService = aService;
    }
}

结果 :Spring 会抛出 BeanCurrentlyInCreationException

1.2 Setter/Field 循环依赖(可以解决)

java 复制代码
@Service
public class AService {
    @Autowired
    private BService bService;  // 字段注入
}

@Service
public class BService {
    @Autowired
    private AService aService;  // 字段注入
}

2. 三级缓存机制

Spring 通过三级缓存来解决 Setter/Field 循环依赖:

2.1 三级缓存结构

java 复制代码
public class DefaultSingletonBeanRegistry {
    // 一级缓存:完整的 Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:早期的 Bean(已实例化但未完成属性注入)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 三级缓存:ObjectFactory 缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

3. 详细解决流程

3.1 AService 和 BService 创建过程

java 复制代码
// 步骤1:开始创建 AService
1. getBean("aService") → createBean("aService")
2. doCreateBean("aService"):
   - 实例化 AService 对象(调用构造函数)
   - 将 AService 包装成 ObjectFactory 放入三级缓存
   - 代码:addSingletonFactory("aService", () -> getEarlyBeanReference(...))

// 此时缓存状态:
// 一级缓存:{}
// 二级缓存:{}  
// 三级缓存:{"aService": ObjectFactory}

// 步骤2:填充 AService 属性
3. populateBean("aService") → 发现需要 bService
4. getBean("bService") → createBean("bService")

// 步骤3:开始创建 BService  
5. doCreateBean("bService"):
   - 实例化 BService 对象
   - 将 BService 包装成 ObjectFactory 放入三级缓存

// 此时缓存状态:
// 一级缓存:{}
// 二级缓存:{}
// 三级缓存:{"aService": ObjectFactory, "bService": ObjectFactory}

// 步骤4:填充 BService 属性
6. populateBean("bService") → 发现需要 aService
7. getBean("aService") → getSingleton("aService")

// 关键步骤:获取 AService 的早期引用
8. getSingleton("aService") 执行过程:
   - 一级缓存:未找到
   - 二级缓存:未找到
   - 三级缓存:找到 ObjectFactory
   - 调用 objectFactory.getObject() 获取 AService 早期引用
   - 将 AService 从三级缓存升级到二级缓存

// 此时缓存状态:
// 一级缓存:{}
// 二级缓存:{"aService": AService@早期引用}
// 三级缓存:{"bService": ObjectFactory}

// 步骤5:继续 BService 创建
9. 将 AService 早期引用注入到 BService
10. BService 完成初始化,放入一级缓存

// 此时缓存状态:
// 一级缓存:{"bService": BService@完整对象}
// 二级缓存:{"aService": AService@早期引用}
// 三级缓存:{}

// 步骤6:继续 AService 创建
11. 将完整的 BService 注入到 AService
12. AService 完成初始化,放入一级缓存

// 最终缓存状态:
// 一级缓存:{"aService": AService@完整对象, "bService": BService@完整对象}
// 二级缓存:{}
// 三级缓存:{}

3.2 核心源码解析

java 复制代码
// 获取单例 Bean 的核心方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存查找完整 Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存查找早期 Bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取 ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 创建早期引用(可能返回代理对象)
                    singletonObject = singletonFactory.getObject();
                    // 升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

// 添加 ObjectFactory 到三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

4. AOP 代理的特殊处理

4.1 为什么需要三级缓存?

java 复制代码
// ObjectFactory 的创建
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // 如果 Bean 需要代理,这里会返回代理对象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

关键点

  • 三级缓存中的 ObjectFactory 可以处理 AOP 代理
  • 如果直接使用二级缓存,无法处理代理对象的循环依赖
  • ObjectFactory 确保在整个过程中,同一个 Bean 名称返回的是同一个代理对象

5. 无法解决的循环依赖场景

5.1 构造器注入

java 复制代码
@Component
public class CircularA {
    private final CircularB circularB;
    
    public CircularA(CircularB circularB) {  // 构造器注入
        this.circularB = circularB;
    }
}

@Component
public class CircularB {
    private final CircularA circularA;
    
    public CircularB(CircularA circularA) {  // 构造器注入
        this.circularA = circularA;
    }
}

原因:Bean 在实例化阶段就需要依赖对象,但此时 Bean 还未创建,无法放入三级缓存。

5.2 原型作用域的 Bean

java 复制代码
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeA {
    @Autowired
    private PrototypeB prototypeB;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeB {
    @Autowired
    private PrototypeA prototypeA;
}

原因:原型 Bean 不会放入缓存,每次都会创建新实例。

5.3 @Async 方法的循环依赖

java 复制代码
@Service
public class AsyncServiceA {
    @Autowired
    private AsyncServiceB serviceB;
    
    @Async
    public void asyncMethod() {
        // 异步方法
    }
}

@Service
public class AsyncServiceB {
    @Autowired
    private AsyncServiceA serviceA;
}

原因:@Async 会创建代理对象,可能在某些情况下导致循环依赖解决失败。

6. 解决方案和最佳实践

6.1 避免循环依赖的设计

java 复制代码
// 好的设计:使用接口解耦
public interface UserService {
    User findUser(Long id);
}

public interface OrderService {
    Order findOrder(Long id);
}

@Service
public class UserServiceImpl implements UserService {
    // 不直接依赖具体实现
    @Autowired
    private OrderService orderService;
}

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

6.2 使用 @Lazy 注解

java 复制代码
@Service
public class ServiceA {
    @Lazy  // 延迟注入
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

6.3 使用 setter 注入

java 复制代码
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) {  // setter 注入
        this.serviceB = serviceB;
    }
}

6.4 使用 ApplicationContextAware

java 复制代码
@Service
public class ServiceA implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    
    public void doSomething() {
        // 在需要的时候再获取 Bean
        ServiceB serviceB = applicationContext.getBean(ServiceB.class);
    }
}

7. 调试技巧

7.1 查看循环依赖信息

java 复制代码
@SpringBootApplication
public class Application implements CommandLineRunner {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Override
    public void run(String... args) {
        // 检查是否存在循环依赖
        if (applicationContext instanceof ConfigurableApplicationContext) {
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) 
                ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
            
            // 查看正在创建的 Bean
            Set<String> singletonsInCreation = beanFactory.getSingletonsCurrentlyInCreation();
            System.out.println("正在创建的Bean: " + singletonsInCreation);
        }
    }
}

8. 总结

Spring 通过三级缓存机制优雅地解决了单例 Bean 的字段/Setter 注入循环依赖问题:

  1. 三级缓存分工

    • 一级缓存:存储完整的 Bean
    • 二级缓存:存储早期的 Bean 引用
    • 三级缓存:存储 ObjectFactory,支持 AOP 代理
  2. 解决时机:在 Bean 实例化后、属性注入前暴露引用

  3. 限制条件

    • 只适用于单例 Bean
    • 只适用于字段注入和 Setter 注入
    • 不适用于构造器注入
  4. 最佳实践

    • 尽量避免循环依赖
    • 使用接口解耦
    • 必要时使用 @Lazy 注解

理解 Spring 的循环依赖解决机制,有助于我们设计更健壮的应用程序架构。





3️⃣ 参考 3

Spring 如何解决循环依赖

什么是循环依赖

循环依赖是指两个或多个 Bean 相互依赖,形成闭环:

java 复制代码
// 情况1:直接相互依赖
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

// 情况2:间接循环依赖
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private C c;
}

@Component
public class C {
    @Autowired
    private A a;
}

Spring 的解决方案

核心机制:三级缓存

Spring 通过三级缓存机制解决循环依赖问题:

java 复制代码
public class DefaultSingletonBeanRegistry {
    // 一级缓存:完整Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    // 二级缓存:早期Bean
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    // 三级缓存:ObjectFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
}

解决流程详解

场景:A ↔ B 相互依赖

步骤1:开始创建 A

java 复制代码
// 1. 调用 getBean("A")
// 2. 标记 A 正在创建中
beforeSingletonCreation("A");

// 3. 实例化 A(调用构造方法)
Object A = createBeanInstance("A");

// 4. 将 A 的 ObjectFactory 放入三级缓存
addSingletonFactory("A", () -> getEarlyBeanReference("A", mbd, A));

// 5. 开始属性注入,发现需要 B
populateBean("A");

步骤2:A 需要 B,开始创建 B

java 复制代码
// 1. 调用 getBean("B")
// 2. 标记 B 正在创建中
beforeSingletonCreation("B");

// 3. 实例化 B(调用构造方法)
Object B = createBeanInstance("B");

// 4. 将 B 的 ObjectFactory 放入三级缓存
addSingletonFactory("B", () -> getEarlyBeanReference("B", mbd, B));

// 5. 开始属性注入,发现需要 A
populateBean("B");

步骤3:B 获取 A 的早期引用

java 复制代码
// B 在 populateBean 时调用 getBean("A")
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 第一次查找:一级缓存(没有A,因为A还没创建完成)
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 第二次查找:二级缓存(没有A)
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 第三次查找:三级缓存(找到A的ObjectFactory)
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过ObjectFactory获取A的早期引用
                    singletonObject = singletonFactory.getObject();
                    // 将A从三级缓存移动到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

步骤4:B 完成创建

java 复制代码
// B 获取到 A 的早期引用后,完成属性注入
// 然后完成 B 的初始化(@PostConstruct, InitializingBean等)
// 最后将 B 放入一级缓存
addSingleton("B", B);

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
    }
}

步骤5:A 完成创建

java 复制代码
// A 继续属性注入,现在可以获取到完整的 B
// 完成 A 的初始化
// 将 A 放入一级缓存
addSingleton("A", A);

源码实现细节

关键方法解析

doCreateBean 方法

java 复制代码
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 1. 实例化
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();
    
    // 2. 判断是否支持早期暴露
    boolean earlySingletonExposure = (mbd.isSingleton() && 
        this.allowCircularReferences && 
        isSingletonCurrentlyInCreation(beanName));
    
    if (earlySingletonExposure) {
        // 3. 添加到三级缓存(关键步骤!)
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    
    // 4. 属性注入(可能触发循环依赖)
    Object exposedObject = bean;
    populateBean(beanName, mbd, instanceWrapper);
    
    // 5. 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    // 6. 处理早期引用
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
        }
    }
    
    return exposedObject;
}

addSingletonFactory 方法

java 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            // 清除二级缓存
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

特殊情况处理

1. 有 AOP 代理的情况

java 复制代码
@Component
public class A {
    @Autowired
    private B b;
    
    public void businessMethod() {
        // 业务逻辑
    }
}

@Aspect
@Component
public class MyAspect {
    @Before("execution(* A.businessMethod(..))")
    public void beforeAdvice() {
        System.out.println("Before method execution");
    }
}

处理机制

  • getEarlyBeanReference 方法中会调用 AOP 相关的后处理器
  • 返回代理对象而不是原始对象
java 复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // AOP 在这里创建代理对象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

2. @Async 方法的情况

java 复制代码
@Component
public class A {
    @Autowired
    private B b;
    
    @Async
    public void asyncMethod() {
        // 异步方法
    }
}

@Component
public class B {
    @Autowired
    private A a;
}

处理 :同样通过 getEarlyBeanReference 方法创建代理

无法解决的循环依赖场景

1. 构造器注入的循环依赖

java 复制代码
@Component
public class A {
    private B b;
    
    @Autowired
    public A(B b) {  // 构造器注入
        this.b = b;
    }
}

@Component
public class B {
    private A a;
    
    @Autowired
    public B(A a) {  // 构造器注入
        this.a = a;
    }
}

原因:在构造器阶段就需要完整的依赖对象,此时 Bean 还未实例化,无法放入三级缓存。

错误信息

vbnet 复制代码
BeanCurrentlyInCreationException: Error creating bean with name 'A': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

2. 原型作用域的循环依赖

java 复制代码
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeA {
    @Autowired
    private PrototypeB b;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeB {
    @Autowired
    private PrototypeA a;
}

原因:原型 Bean 不缓存,每次都会创建新实例。

3. @Configuration 类的循环依赖

java 复制代码
@Configuration
public class ConfigA {
    @Autowired
    private ConfigB configB;
}

@Configuration
public class ConfigB {
    @Autowired
    private ConfigA configA;
}

解决方案和最佳实践

1. 代码重构方案

方案1:使用 setter 注入代替构造器注入

java 复制代码
@Component
public class A {
    private B b;
    
    @Autowired
    public void setB(B b) {  // setter注入
        this.b = b;
    }
}

方案2:使用 @Lazy 注解

java 复制代码
@Component
public class A {
    private final B b;
    
    @Autowired
    public A(@Lazy B b) {  // 延迟加载
        this.b = b;
    }
}

方案3:使用 ApplicationContextAware

java 复制代码
@Component
public class A implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    
    public void someMethod() {
        // 在需要的时候再获取B
        B b = applicationContext.getBean(B.class);
    }
}

方案4:重新设计代码结构

java 复制代码
// 提取公共逻辑到第三个类
@Component
public class ServiceFacade {
    @Autowired
    private A a;
    
    @Autowired
    private B b;
    
    public void commonBusiness() {
        a.doSomething();
        b.doSomething();
    }
}

2. Spring 配置方案

开启循环依赖支持(默认开启)

java 复制代码
@Configuration
public class AppConfig {
    // Spring Boot 2.6+ 默认关闭循环依赖
    // 需要手动开启
    @Bean
    public AbstractBeanFactory abstractBeanFactory() {
        return new DefaultListableBeanFactory() {
            {
                setAllowCircularReferences(true);
            }
        };
    }
}

调试和排查技巧

1. 查看循环依赖信息

java 复制代码
@Component
public class CircularDependencyDetector implements ApplicationContextAware {
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            ConfigurableListableBeanFactory beanFactory = 
                ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
            
            if (beanFactory instanceof DefaultListableBeanFactory) {
                // 可以查看三级缓存状态
                System.out.println("检测循环依赖...");
            }
        }
    }
}

2. 使用 Spring Boot Actuator

yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: beans
  endpoint:
    beans:
      enabled: true

访问:http://localhost:8080/actuator/beans 查看 Bean 依赖关系

总结

Spring 通过三级缓存机制优雅地解决了循环依赖问题:

  1. 三级缓存分工明确

    • 一级缓存:完整的 Bean
    • 二级缓存:早期的 Bean(避免重复创建代理)
    • 三级缓存:ObjectFactory(创建早期引用)
  2. 解决条件

    • 必须是单例 Bean
    • 不能是构造器注入
    • 必须通过 setter/字段注入
  3. 设计思想

    • 提前暴露引用
    • 分离实例化、依赖注入、初始化阶段
    • 支持 AOP 等增强功能

理解 Spring 的循环依赖解决机制,有助于编写更健壮的代码和更好地排查相关问题。





4️⃣ 参考 4

Spring 如何解决循环依赖

1. 循环依赖的定义和类型

1.1 什么是循环依赖 循环依赖是指两个或多个 Bean 相互依赖,形成闭环:

  • A 依赖 B
  • B 依赖 A

或者更复杂的:

  • A 依赖 B
  • B 依赖 C
  • C 依赖 A

1.2 循环依赖的类型

java 复制代码
// 1. 字段注入循环依赖(Spring 可以解决)
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

// 2. setter 方法注入循环依赖(Spring 可以解决)
@Component
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

// 3. 构造器注入循环依赖(Spring 无法解决)
@Component
public class ServiceA {
    private final ServiceB serviceB;
    
    @Autowired
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

2. Spring 解决循环依赖的核心机制

2.1 三级缓存架构

Spring 通过三级缓存解决循环依赖问题:

java 复制代码
public class DefaultSingletonBeanRegistry {
    // 一级缓存:完整 Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:早期 Bean(已实例化但未初始化)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 三级缓存:ObjectFactory(用于创建早期引用)
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    // 正在创建中的 Bean
    private final Set<String> singletonsCurrentlyInCreation = 
        Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

2.2 解决流程详细分析

示例场景:ServiceA ↔ ServiceB 循环依赖

java 复制代码
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
    
    public void methodA() {
        System.out.println("ServiceA method");
    }
}

@Service  
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
    
    public void methodB() {
        System.out.println("ServiceB method");
    }
}

解决步骤

  1. 开始创建 ServiceA

    java 复制代码
    // 标记 A 正在创建
    singletonsCurrentlyInCreation.add("serviceA");
    
    // 实例化 ServiceA(调用构造函数)
    ServiceA a = new ServiceA();
    
    // 将 ServiceA 的 ObjectFactory 放入三级缓存
    addSingletonFactory("serviceA", () -> getEarlyBeanReference("serviceA", mbd, a));
  2. ServiceA 属性注入,发现需要 ServiceB

    java 复制代码
    // 在 populateBean 阶段,发现 @Autowired ServiceB
    // 调用 getBean("serviceB") 获取 ServiceB
  3. 开始创建 ServiceB

    java 复制代码
    // 标记 B 正在创建
    singletonsCurrentlyInCreation.add("serviceB");
    
    // 实例化 ServiceB
    ServiceB b = new ServiceB();
    
    // 将 ServiceB 的 ObjectFactory 放入三级缓存
    addSingletonFactory("serviceB", () -> getEarlyBeanReference("serviceB", mbd, b));
  4. ServiceB 属性注入,发现需要 ServiceA

    java 复制代码
    // 调用 getBean("serviceA"),发现 serviceA 正在创建
    // 从三级缓存获取 ServiceA 的早期引用
    ObjectFactory<?> singletonFactory = this.singletonFactories.get("serviceA");
    if (singletonFactory != null) {
        // 创建早期引用(可能经过 AOP 处理)
        Object earlyReference = singletonFactory.getObject();
        
        // 将早期引用放入二级缓存,移除三级缓存
        this.earlySingletonObjects.put("serviceA", earlyReference);
        this.singletonFactories.remove("serviceA");
        
        // ServiceB 成功注入 ServiceA 的早期引用
    }
  5. ServiceB 完成创建

    java 复制代码
    // ServiceB 完成属性注入、初始化等后续步骤
    // 将完整的 ServiceB 放入一级缓存
    addSingleton("serviceB", b);
    
    // 清理缓存
    this.earlySingletonObjects.remove("serviceB");
    this.singletonFactories.remove("serviceB");
  6. ServiceA 完成创建

    java 复制代码
    // ServiceA 继续属性注入(此时 ServiceB 已可用)
    // ServiceA 完成初始化
    // 将完整的 ServiceA 放入一级缓存
    addSingleton("serviceA", a);
    
    // 清理缓存
    this.earlySingletonObjects.remove("serviceA");
    this.singletonFactories.remove("serviceA");

3. 源码关键方法解析

3.1 getSingleton 方法(核心查找逻辑)

java 复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存查找完整 Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存查找早期 Bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取 ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 创建早期引用
                    singletonObject = singletonFactory.getObject();
                    // 升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

3.2 doCreateBean 方法(创建流程)

java 复制代码
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 1. 实例化
    Object beanInstance = createBeanInstance(beanName, mbd, args);
    
    // 2. 判断是否允许早期暴露
    boolean earlySingletonExposure = (mbd.isSingleton() && 
                                      this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 3. 添加到三级缓存(解决循环依赖的关键)
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, beanInstance));
    }
    
    // 4. 属性注入(可能触发循环依赖)
    populateBean(beanName, mbd, instanceWrapper);
    
    // 5. 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    return exposedObject;
}

3.3 addSingletonFactory 方法

java 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

4. 特殊情况处理

4.1 AOP 代理对象的循环依赖

java 复制代码
@Service
public class UserService {
    @Autowired
    private OrderService orderService;
    
    @Transactional
    public void createUser() {
        // 业务逻辑
    }
}

@Service
public class OrderService {
    @Autowired
    private UserService userService;  // 需要的是 UserService 的代理对象
}

处理机制

java 复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = 
                    (SmartInstantiationAwareBeanPostProcessor) bp;
                // AbstractAutoProxyCreator 会在这里返回代理对象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

4.2 多级循环依赖

java 复制代码
@Service
public class ServiceA {
    @Autowired private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired private ServiceC serviceC;  
}

@Service
public class ServiceC {
    @Autowired private ServiceA serviceA;  // 三级循环依赖
}

处理流程

  1. A 创建 → 需要 B
  2. B 创建 → 需要 C
  3. C 创建 → 需要 A(从三级缓存获取 A 的早期引用)
  4. C 完成 → B 完成 → A 完成

5. Spring 无法解决的循环依赖场景

5.1 构造器注入循环依赖

java 复制代码
@Component
public class CircularA {
    private final CircularB circularB;
    
    @Autowired
    public CircularA(CircularB circularB) {  // 构造器注入
        this.circularB = circularB;
    }
}

@Component
public class CircularB {
    private final CircularA circularA;
    
    @Autowired
    public CircularB(CircularA circularA) {  // 构造器注入
        this.circularA = circularA;
    }
}

错误信息

vbnet 复制代码
BeanCurrentlyInCreationException: 
Error creating bean with name 'circularA': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

原因:构造器注入在实例化阶段就需要完整依赖,此时 Bean 还未放入三级缓存。

5.2 Prototype 作用域的循环依赖

java 复制代码
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeA {
    @Autowired
    private PrototypeB prototypeB;
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  
public class PrototypeB {
    @Autowired
    private PrototypeA prototypeA;
}

原因:Prototype Bean 不缓存,每次都会新建,无法通过缓存机制解决循环依赖。

5.3 @Async 方法的循环依赖

java 复制代码
@Service
public class AsyncServiceA {
    @Autowired
    private AsyncServiceB serviceB;
    
    @Async
    public void asyncMethod() {
        // 异步方法
    }
}

@Service
public class AsyncServiceB {
    @Autowired
    private AsyncServiceA serviceA;  // 可能存在问题
}

6. 解决方案和最佳实践

6.1 避免循环依赖的设计

java 复制代码
// 好的设计:使用接口解耦
public interface UserValidator {
    boolean validate(User user);
}

@Service
public class BasicUserValidator implements UserValidator {
    // 不依赖其他业务Service
}

@Service  
public class UserService {
    @Autowired
    private List<UserValidator> validators;  // 依赖接口而非具体实现
}

6.2 使用 @Lazy 注解

java 复制代码
@Service
public class ServiceA {
    @Lazy  // 延迟注入,打破循环
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

6.3 使用 setter 注入 + @Autowired(required = false)

java 复制代码
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired(required = false)
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

6.4 使用 ApplicationContextAware

java 复制代码
@Service
public class ServiceA implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    
    public void doSomething() {
        ServiceB serviceB = applicationContext.getBean(ServiceB.class);
        // 使用 serviceB
    }
}

7. 总结

Spring 通过三级缓存机制优雅地解决了循环依赖问题:

  • 一级缓存:存储完整的单例 Bean
  • 二级缓存:存储早期的 Bean 引用
  • 三级缓存:存储 Bean 工厂,用于创建早期引用(支持 AOP 代理)

关键点

  1. 只有单例 Bean + setter/字段注入才能解决循环依赖
  2. 构造器注入的循环依赖无法解决
  3. Prototype Bean的循环依赖无法解决
  4. 合理设计架构,避免循环依赖是最佳实践

设计启示:Spring 的三级缓存体现了"空间换时间"和"关注点分离"的设计思想,通过缓存分层解决了复杂的依赖关系问题。





5️⃣ 参考 5

Spring 通过 三级缓存机制提前暴露对象引用 的方式,优雅地解决了单例 Bean 之间的循环依赖问题。以下是其核心原理和详细流程:


一、循环依赖的场景 当两个或多个 Bean 相互依赖时,会形成循环依赖:

  • 场景示例
    • Bean A 依赖 Bean B(A → B
    • Bean B 依赖 Bean A(B → A
  • 问题:如果按顺序创建 Bean,会导致死锁(A 等待 B 初始化完成,B 又等待 A 初始化完成)。

二、Spring 解决循环依赖的核心机制 Spring 通过 三级缓存提前暴露对象引用 打破循环依赖的死锁:

1. 三级缓存的结构

缓存层级 存储内容 作用
一级缓存 SingletonObjects(完全初始化的 Bean) 存储已完整初始化的单例 Bean,可直接使用。
二级缓存 EarlySingletonObjects(半成品 Bean) 存储已实例化但未完全初始化的 Bean(属性未注入),用于循环依赖场景。
三级缓存 SingletonFactories(ObjectFactory) 存储 Bean 的工厂对象,用于生成代理对象或原始对象(解决 AOP 代理问题)。

2. 关键流程A → B → A 的循环依赖为例,说明 Spring 的处理步骤:

(1)创建 Bean A

  1. Spring 从一级缓存查找 Bean A,未找到。
  2. 创建 Bean A 的实例(调用构造器),并将其包装成 ObjectFactory 存入 三级缓存
  3. 开始属性注入,发现依赖 Bean B。

(2)创建 Bean B

  1. Spring 从一级缓存查找 Bean B,未找到。
  2. 创建 Bean B 的实例,并将其包装成 ObjectFactory 存入 三级缓存
  3. 开始属性注入,发现依赖 Bean A。

(3)解决 Bean A 的循环依赖

  1. Spring 从一级缓存查找 Bean A,未找到;从二级缓存查找,未找到。
  2. 三级缓存 获取 Bean A 的 ObjectFactory,调用 getObject() 生成 半成品 Bean A(可能是代理对象)。
  3. 将半成品 Bean A 存入 二级缓存,并从三级缓存移除。
  4. 将半成品 Bean A 注入 Bean B。

(4)完成 Bean B 的初始化

  1. Bean B 完成属性注入和初始化(如调用 init-method)。
  2. 将完全初始化的 Bean B 存入 一级缓存,并从二级缓存移除。

(5)完成 Bean A 的初始化

  1. Spring 继续 Bean A 的属性注入,从一级缓存获取完全初始化的 Bean B。
  2. Bean A 完成初始化,存入 一级缓存,并从二级缓存移除。

三、为什么需要三级缓存? 1. 解决 AOP 代理问题

  • 如果 Bean 需要被代理(如 @Transactional@Async),三级缓存的 ObjectFactory 可以按需生成 代理对象原始对象
  • 例如:Bean A 需要被代理,工厂对象会返回代理对象而非原始对象。

2. 避免重复创建

  • 三级缓存通过 ObjectFactory 延迟对象生成,确保单例 Bean 的唯一性。
  • 如果直接存储半成品 Bean(二级缓存),可能无法处理代理场景。

3. 性能优化

  • 半成品 Bean 仅在需要时生成(如循环依赖时),减少不必要的初始化开销。

四、源码解析 关键方法位于 DefaultSingletonBeanRegistry 类中:

1. 获取单例 Bean( getSingleton )

java 复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 2. 从二级缓存获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 3. 从三级缓存获取工厂对象,生成半成品 Bean
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 升级到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

2. 添加工厂对象到三级缓存( addSingletonFactory )

java 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 将工厂对象存入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            // 标记 Bean 正在创建中
            this.registeredSingletons.add(beanName);
        }
    }
}

五、限制与注意事项 1. 仅支持单例 Bean

  • 多例(Prototype)Bean 无法通过三级缓存解决循环依赖,因为每次请求都会创建新实例。

2. 构造器注入的局限性

  • 如果循环依赖通过 构造器注入 (如 public A(B b) {}),三级缓存无法解决,会抛出 BeanCurrentlyInCreationException
  • 解决方案 :改用 Setter 注入字段注入

3. 代理对象的兼容性

  • 如果 Bean 未被代理,三级缓存可简化为二级缓存(直接存储半成品 Bean)。
  • 但 Spring 为统一处理代理场景,始终使用三级缓存。

六、总结

机制 作用
三级缓存 分阶段存储 Bean 的不同状态(原始对象、半成品、完全初始化)。
提前暴露引用 通过三级缓存的工厂对象,在 Bean 初始化完成前暴露其引用,打破循环。
AOP 代理支持 工厂对象按需生成代理对象,确保代理逻辑正确执行。

Spring 的三级缓存机制通过 分阶段缓存延迟对象生成,高效地解决了单例 Bean 的循环依赖问题,同时兼容 AOP 代理场景。理解这一机制有助于优化 Bean 设计,避免不必要的循环依赖。





6️⃣ 参考 6

Spring如何解决循环依赖

Spring通过三级缓存机制来解决循环依赖问题,这是Spring IoC容器的核心设计之一。下面我将详细解释Spring解决循环依赖的原理和流程。

三级缓存的组成

Spring的三级缓存包括:

  1. 一级缓存 (singletonObjects)

    • 存储完全初始化完成的单例Bean
    • 是最终可以被使用的Bean实例
    • 通过ConcurrentHashMap实现
  2. 二级缓存 (earlySingletonObjects)

    • 存储已实例化但未初始化的"早期引用"(半成品Bean)
    • 用于解决AOP代理问题
    • 通过HashMap实现
  3. 三级缓存 (singletonFactories)

    • 存储ObjectFactory函数式接口
    • 用于在Bean创建过程中提前暴露未完成的Bean
    • 通过Map<String, ObjectFactory<?>>实现

解决循环依赖的核心原理

Spring解决循环依赖的关键在于提前暴露未完全创建完毕的Bean,而不是等待Bean完全初始化后再处理依赖。

重要限制 :Spring仅能解决Setter注入 方式的循环依赖,不能解决构造器注入的循环依赖。在Spring Boot 3.x中,已不再允许处理构造函数的循环依赖。

解决循环依赖的流程(以A依赖B,B依赖A为例)

  1. 创建A

    • Spring先实例化A(A是半成品,属性未注入)
    • 将A的ObjectFactory放入三级缓存(singletonFactories)
  2. A需要注入B

    • 开始实例化B
    • 在B的创建过程中,需要注入A
  3. B查找A

    • B在一级缓存中找不到A
    • 在二级缓存中也找不到A
    • 从三级缓存中获取A的ObjectFactory
    • 通过ObjectFactory创建A的半成品对象
    • 将A的半成品对象放入二级缓存(earlySingletonObjects)
  4. B完成初始化

    • B完成属性注入和初始化
    • B放入一级缓存(singletonObjects)
  5. A继续完成

    • A从二级缓存中获取到B
    • A完成属性注入和初始化
    • A放入一级缓存
    • 从三级缓存中移除A的ObjectFactory

为什么需要三级缓存?

  • 如果只有两级缓存(一级和二级),在AOP场景下会导致问题
  • 三级缓存提供了一个"工厂",可以在需要时生成代理对象
  • 保证了在Bean创建过程中可以提前暴露半成品,解决循环依赖

重要注意事项

  1. 适用范围

    • 仅适用于Singleton作用域的Bean
    • 无法解决构造器注入的循环依赖
    • 无法解决原型Bean(Prototype)的循环依赖
  2. AOP代理处理

    • getEarlyBeanReference方法中,如果有AOP配置,会在此生成代理对象
    • 三级缓存允许在Bean创建过程中提前生成代理
  3. 设计建议

    • Spring仍然鼓励避免循环依赖,因为它会增加代码复杂性
    • 应尽量设计应用结构,避免出现循环依赖

为什么不能用构造器注入解决循环依赖?

构造器注入是在Bean实例化时完成的,而循环依赖需要在实例化后才能解决。当两个Bean都使用构造器注入对方时,Spring无法解决这种循环依赖,会抛出BeanCurrentlyInCreationException异常。

总结

Spring的三级缓存机制通过"提前暴露半成品Bean"的方式,解决了Setter注入方式下的循环依赖问题。它在Bean创建过程中,将未完全初始化的Bean提前暴露,使依赖方可以获取到这个"半成品",从而打破循环依赖。这种设计保证了Bean创建过程的一致性和正确性,是Spring IoC容器的重要特性。

提示 :虽然Spring能解决循环依赖,但循环依赖通常表明设计问题,应尽量避免。如果必须使用循环依赖,建议使用@Lazy注解或重构代码结构来避免。





7️⃣ 参考 7

Spring 主要通过其 三级缓存机制 来解决单例(Singleton)Bean 之间的循环依赖问题。这是 Spring IoC 容器的核心设计之一,确保了在复杂的依赖关系下 Bean 能够被正确创建。

核心前提

Spring 的循环依赖解决方案仅适用于单例(Singleton)作用域的 Bean ,并且是通过setter 注入字段注入 形成的循环依赖。对于构造器注入的循环依赖,或者原型(Prototype)作用域的循环依赖,Spring 无法解决,会直接抛出 BeanCurrentlyInCreationException 异常。

最常见的循环依赖场景是两个 Bean 相互依赖:

  • AService 依赖 BService
  • BService 依赖 AService

解决方案:三级缓存

Spring 使用三个 Map 结构(即"三级缓存")来协同工作:

  1. 一级缓存 (singletonObjects):

    • 存放完全初始化完成的单例 Bean 实例。
    • 这是最终的、可直接使用的 Bean 仓库。
  2. 二级缓存 (earlySingletonObjects):

    • 存放提前暴露的、原始的 Bean 实例。
    • 这些实例已经通过构造函数创建(实例化),但尚未进行属性填充和初始化
  3. 三级缓存 (singletonFactories):

    • 存放能够创建早期 Bean 实例的工厂(ObjectFactory)。
    • 这个工厂的核心作用是在需要时生成 Bean 的早期引用,特别关键的是它能处理 AOP 代理的创建

工作流程详解

AServiceBService 循环依赖为例:

  1. 开始创建 AService

    • Spring 检查一级、二级缓存都不存在 AService
    • 开始创建 AService,调用其构造函数,实例化完成
    • 此时 AService 对象已存在,但它的属性(如 bService)还未注入。
  2. 暴露 AService 的早期引用

    • 为了打破可能的循环依赖,Spring 将一个 ObjectFactory(工厂)放入三级缓存
    • 这个工厂的作用是:当被调用时,可以返回 AService 的早期引用。如果 AService 需要 AOP 代理,这个工厂会在这里创建代理对象。
  3. 注入 AService 的依赖 (BService)

    • AService 需要注入 BService,于是 Spring 开始创建 BService
    • BService 经历同样的过程:实例化完成,并将其 ObjectFactory 放入三级缓存。
  4. BService 需要注入 AService (循环点)

    • 当为 BService 注入 AService 时,Spring 查找 AService
    • 一级、二级缓存都没有,但在三级缓存 中找到了 AServiceObjectFactory
    • Spring 调用这个工厂的 getObject() 方法
    • 工厂方法内部执行 getEarlyBeanReference()
      • 如果 AService 需要代理(如 @Transactional),则在此刻创建代理对象。
      • 如果不需要,则返回原始的 AService 实例。
    • 将得到的早期引用(可能是代理对象)放入二级缓存
    • 从三级缓存中移除 AServiceObjectFactory(保证只创建一次)。
    • 将这个早期引用注入到 BService 中。
  5. 完成 BService 的创建

    • BService 完成所有属性的注入和初始化回调。
    • 将完整的 BService 放入一级缓存
    • 从二级、三级缓存中清理 BService 的相关条目。
  6. 完成 AService 的创建

    • 回到 AService 的创建流程,此时 BService 已经创建完毕。
    • BService 注入到 AService 中。
    • AService 完成初始化,成为一个完整的 Bean。
    • AService 放入一级缓存
    • 清理其在二级缓存中的早期引用。

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

这是关键所在。如果只有二级缓存,在 AService 实例化后就直接把原始对象放进二级缓存。当 BService 需要时,会拿到这个原始对象

但如果 AService 是一个需要事务管理的 Service,那么最终容器里应该是一个代理对象。这就导致了不一致:

  • BService 持有的是 AService原始对象
  • 容器最终提供的是 AService代理对象

这破坏了单例原则,也使得 AOP 功能失效。

三级缓存的精妙之处在于 : 它不直接存放对象,而是存放一个工厂 。这个工厂可以在被调用时动态决定返回原始对象还是代理对象。这样,无论是早期注入还是最终使用,所有地方引用的都是同一个最终对象(要么都是原始对象,要么都是代理对象),保证了对象一致性。

总结

Spring 通过 三级缓存 解决单例 Bean 的循环依赖:

  1. 在 Bean 实例化后 ,立即向三级缓存注册一个 ObjectFactory
  2. 当出现循环依赖时,通过三级缓存的工厂获取早期引用,并将其放入二级缓存。
  3. 最终 Bean 创建完成后,放入一级缓存,并清理低级缓存。

这一机制不仅解决了循环依赖,还巧妙地与 AOP 集成,确保了代理对象的一致性,体现了 Spring 设计的高超之处。





8️⃣ 参考 8

Spring解决循环依赖的核心机制是三级缓存 ,但这一机制仅针对单例Bean的setter注入/字段注入场景。循环依赖指多个Bean之间互相依赖(如A依赖B,B依赖A),若不处理会导致创建过程陷入无限循环。以下是Spring解决循环依赖的具体逻辑:

一、循环依赖的分类与Spring的处理范围 首先明确Spring能解决和不能解决的循环依赖类型:

  • 能解决 :单例Bean之间通过setter方法注入字段注入形成的循环依赖。
  • 不能解决
    • 原型Bean(scope="prototype")的循环依赖(每次创建都是新实例,无法缓存);
    • 单例Bean之间通过构造器注入形成的循环依赖(实例化前无法提前暴露对象)。

二、核心解决方案:三级缓存的协同工作 Spring通过三级缓存(按"Bean完成度"从高到低)实现单例Bean循环依赖的破解,核心是**"提前暴露未完全初始化的Bean"**,让依赖方先拿到引用,后续再完善Bean。

  1. 三级缓存的定义
  • 一级缓存(singletonObjects) :存储完全初始化完成的单例Bean(已实例化、属性注入、初始化方法执行完毕),是最终供使用的Bean。
  • 二级缓存(earlySingletonObjects) :存储提前暴露的早期Bean(已实例化,但未完成属性注入和初始化),可能是原始对象或AOP代理对象。
  • 三级缓存(singletonFactories) :存储Bean的工厂对象(ObjectFactory),用于"按需生成"早期Bean(若需要AOP代理,工厂会生成代理对象;否则返回原始对象)。
  1. 解决循环依赖的流程(以A依赖B,B依赖A为例) 假设A和B都是单例Bean,且通过setter注入依赖:

步骤1:创建Bean A

  • 检查一级缓存(A不存在),开始创建A。
  • 实例化A(调用构造器生成原始对象,此时A未注入属性、未执行初始化方法)。
  • 将A的工厂对象(ObjectFactory)放入三级缓存:工厂逻辑为"若A需要AOP代理,则生成代理对象;否则返回原始A"。
  • 开始为A注入属性,发现依赖B,触发Bean B的创建。

步骤2:创建Bean B

  • 检查一级缓存(B不存在),开始创建B。
  • 实例化B,将B的工厂对象放入三级缓存。
  • 为B注入属性,发现依赖A,开始查找A。

步骤3:B获取A的早期实例,打破循环

  • 查找A时,一级缓存(A未完成)和二级缓存(无)均无,最终从三级缓存找到A的工厂。
  • 通过A的工厂生成A的早期实例(若A需要代理,此处生成代理对象),并将该实例从三级缓存移至二级缓存(标记为"已提前暴露")。
  • 将A的早期实例注入B,B完成属性注入和初始化,放入一级缓存(此时B是完整的)。

步骤4:A完成创建

  • B创建完成后,回到A的属性注入步骤,从一级缓存取出B并注入A。
  • A完成属性注入和初始化,放入一级缓存(此时A是完整的),同时从二级缓存移除A的早期实例。

三、为什么需要三级缓存? 核心是为了处理AOP代理场景下的循环依赖

  • 若仅用两级缓存(一级+二级),提前暴露的只能是A的原始对象。但如果A需要AOP代理(如被@Transactional注解),最终容器中的A是代理对象,而B注入的却是原始A,会导致对象不一致。
  • 三级缓存的工厂(ObjectFactory)实现了"延迟生成代理":只有当循环依赖发生时,才会通过工厂生成代理对象并放入二级缓存,既保证了注入的是正确的代理对象,又避免了无循环依赖时不必要的代理创建。

四、无法解决的循环依赖场景

  1. 构造器循环依赖

    若A的构造器依赖B,B的构造器依赖A,由于实例化(构造器调用)是创建Bean的第一步,此时A和B均未实例化,无法生成工厂对象放入三级缓存,因此会直接抛出BeanCurrentlyInCreationException

  2. 原型Bean循环依赖

    原型Bean的scopeprototype,每次获取都会创建新实例,Spring不会缓存原型Bean,因此循环依赖时会无限创建新实例,最终报错。

总结 Spring解决单例Bean循环依赖的核心是三级缓存+提前暴露未完全初始化的Bean:通过三级缓存延迟处理AOP代理,二级缓存暂存早期实例,一级缓存存储最终完整Bean,三者协同打破了"先有鸡还是先有蛋"的循环。但该机制仅适用于单例Bean的setter/字段注入,对构造器注入和原型Bean无效。





9️⃣ 参考 9

Spring通过三级缓存机制高效地解决了单例Bean的循环依赖问题,同时AOP代理的正确性。以下是其核心原理和详细流程:


一、循环依赖的类型

  1. 构造器循环依赖

    • 例如:A的构造器依赖BB的构造器又依赖A
    • Spring无法解决 :因为构造器注入时Bean尚未实例化,无法提前暴露引用。需通过@Lazy注解或重构代码避免。
  2. 属性循环依赖

    • 例如:A的属性依赖BB的属性又依赖A
    • Spring可解决:通过三级缓存暴露半成品Bean,允许属性注入。

二、三级缓存机制 Spring通过三个缓存层级管理Bean的不同状态,解决属性循环依赖:

缓存层级 存储内容 作用
一级缓存 完全初始化的Bean(SingletonObjects 存储最终可用的Bean,直接返回给调用者。
二级缓存 半成品Bean(EarlySingletonObjects 存储已实例化但未初始化的Bean,供循环依赖时提前使用。
三级缓存 Bean工厂(SingletonFactories 存储ObjectFactory,用于动态生成Bean(如处理AOP代理时的原始Bean或代理Bean)。

三、解决循环依赖的核心流程A依赖BB依赖A为例:

1. 创建Bean A

  1. 检查一级缓存 :未找到A,继续创建。

  2. 实例化A :调用构造器生成A的原始对象(未注入属性)。

  3. 将A的工厂放入三级缓存

    java 复制代码
    addSingletonFactory("A", () -> getEarlyBeanReference("A", beanA, mbd));
    • getEarlyBeanReference可能返回代理对象(如AOP场景),确保代理的唯一性。

2. 填充A的属性(发现依赖B)

  1. 检查一级缓存 :未找到B,继续创建。
  2. 实例化B :调用构造器生成B的原始对象。
  3. 将B的工厂放入三级缓存
  4. 填充B的属性(发现依赖A)
    • 从三级缓存获取A的工厂,调用getObject()生成A的早期引用(可能是代理对象)。
    • A的早期引用从三级缓存移除,并放入二级缓存。
    • B完成属性注入(使用二级缓存中的A)。

3. 完成B的初始化

  1. 初始化B :调用init-method@PostConstruct等。
  2. 将B放入一级缓存 :后续获取B直接从此缓存返回。
  3. 从二级缓存移除A的早期引用(若未被其他依赖使用)。

4. 完成A的初始化

  1. 继续初始化A :使用一级缓存中的B完成属性注入。
  2. 初始化A :调用init-method@PostConstruct等。
  3. 将A放入一级缓存 :后续获取A直接从此缓存返回。

四、关键代码解析 1. getSingleton 方法

java 复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 从一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 2. 从二级缓存获取
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 3. 从三级缓存获取工厂并生成早期引用
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                // 将早期引用从三级缓存移除,并放入二级缓存
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

2. addSingletonFactory 方法

java 复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 将工厂放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            // 标记Bean正在创建中
            this.registeredSingletons.add(beanName);
        }
    }
}

五、为什么需要三级缓存?

  1. 解决AOP代理问题

    • 若Bean需要被代理(如@Transactional),三级缓存的工厂机制确保代理对象在循环依赖中也能正确生成。
    • 例如:A被代理为AProxy,三级缓存的工厂会返回AProxy,而非原始A
  2. 避免重复创建

    • 二级缓存存储半成品Bean,防止循环依赖时重复实例化。
  3. 线程安全

    • 一级缓存使用ConcurrentHashMap,二级/三级缓存通过同步块保证多线程环境下的正确性。

六、限制与注意事项

  1. 仅支持单例Bean

    • 原型(prototype)Bean无法使用三级缓存,因为每次请求都会创建新实例。
  2. 构造器注入无效

    • 构造器循环依赖需通过@Lazy注解或重构代码解决。
  3. 依赖顺序敏感

    • 循环依赖的Bean必须按特定顺序初始化(如A依赖B时,B不能先初始化完成)。

七、总结 Spring通过三级缓存机制高效解决了属性循环依赖问题:

  1. 一级缓存存储完全初始化的Bean。
  2. 二级缓存存储半成品Bean,供循环依赖时提前使用。
  3. 三级缓存存储工厂,动态生成Bean(如代理对象),确保全局唯一性。

这一设计兼顾了性能、线程安全和AOP兼容性,是Spring框架的核心特性之一。





🔟 参考 10

以下是Spring解决循环依赖的核心方法及技术细节,基于三级缓存机制实现:

一、三级缓存机制(核心解决方案)

  1. 一级缓存 singletonObjects

    ✅ 存储完全初始化的单例Bean,可直接使用

    java 复制代码
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  2. 二级缓存 earlySingletonObjects

    ⚠️ 存储提前暴露的Bean半成品(已实例化但未完成属性注入)

    java 复制代码
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  3. 三级缓存 singletonFactories

    🔧 存储Bean工厂对象(用于生成代理对象)

    java 复制代码
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

二、解决流程(以A→B→A循环依赖为例)

  1. 创建Bean A

    • 实例化A(调用构造器)
    • 将A的工厂对象 放入三级缓存 singletonFactories
    • 开始属性注入(此时发现依赖B)
  2. 创建Bean B

    • 实例化B → 属性注入时发现依赖A
    • 从三级缓存获取A的工厂 → 调用 getObject() 生成A的代理对象
    • 将代理对象放入二级缓存 earlySingletonObjects,同时移除三级缓存的工厂
    • B完成初始化,移入一级缓存
  3. 完成A的注入

    • 将二级缓存中的A代理对象注入B
    • B返回给A的注入流程
    • A完成属性注入 → 移入一级缓存 singletonObjects

三、关键限制条件

  1. 必须都是单例Bean

    java 复制代码
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }  // 原型模式直接报错
  2. 不能全是构造器注入

    • 构造器注入在实例化阶段就需要完整依赖,无法通过提前暴露对象解决
    • 字母序靠前的Bean必须使用setter注入(如A依赖B,则A不能是构造器注入)

四、替代解决方案

  1. Setter/字段注入

    ✅ 允许先创建实例再注入依赖,打破实例化死锁

  2. @Lazy注解

    java 复制代码
    @Autowired
    public A(@Lazy B b) { ... }  // 延迟初始化代理对象
  3. ObjectFactory

    java 复制代码
    @Autowired
    private ObjectFactory<B> bFactory;
    public void useB() {
      B b = bFactory.getObject();  // 按需获取Bean
    }

五、典型异常场景

log 复制代码
BeanCurrentlyInCreationException: 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?

⚠️ 触发条件

  • 原型Bean的循环依赖
  • 纯构造器注入的循环依赖
  • 通过@DependsOn强制指定的循环初始化顺序

提示:Spring Boot 2.6+ 默认禁止循环依赖,可通过 spring.main.allow-circular-references=true 开启。





🤔 参考 11

Spring 解决循环依赖的核心机制是 三级缓存,它通过分层缓存和提前暴露不完全初始化的 Bean 对象,巧妙地打破了循环引用。同时,Spring 也提供了一些注解和编程手段来辅助处理特定场景。下面这张表格汇总了 Spring 三级缓存的核心信息,帮助你快速理解:

缓存层级 名称 存储内容 主要作用
一级缓存 singletonObjects 完全初始化完成的单例Bean 提供最终可用的Bean,是正式的"单例池"
二级缓存 earlySingletonObjects 早期暴露的Bean(已实例化,但未完成初始化) 解决循环依赖,避免多次从工厂创建实例
三级缓存 singletonFactories 创建Bean的工厂对象 (ObjectFactory) 用于延迟创建早期引用,支持AOP代理

Spring 解决循环依赖的核心流程,可以概括为 "实例化 -> 提前暴露 -> 依赖注入 -> 完成初始化" 的过程。下面的流程图描绘了这一核心过程:

flowchart TD A["创建Bean A
getBean(a)"] --> B["实例化A
(调用构造函数)"] B --> C[得到A的原始对象] C --> D["提前暴露A的引用
将 ObjectFactory 放入三级缓存"] D --> E["属性注入:A 依赖 B
触发 getBean(b)"] E --> F["创建Bean B
getBean(b)"] F --> G["实例化B
(调用构造函数)"] G --> H[得到B的原始对象] H --> I["提前暴露B的引用
将 ObjectFactory 放入三级缓存"] I --> J["属性注入:B 依赖 A
触发 getBean(a)"] J --> K{"再次查找A
一级缓存? --> 无
二级缓存? --> 无"} K -- 三级缓存有 --> L["通过ObjectFactory
获取A的早期引用"] L --> M["将A的早期引用放入二级缓存
并从三级缓存移除工厂"] M --> N["B注入A的早期引用成功"] N --> O["B完成初始化
放入一级缓存"] O --> P["回到A的属性注入流程
此时成功获取到完整的B"] P --> Q["A完成初始化"] Q --> R["将A放入一级缓存
并清理二、三级缓存"] R --> S["容器中存在
完全初始化的A和B"]

⚙️ 辅助解决方案与适用场景

三级缓存是Spring的默认机制,但开发者也可以通过一些方式主动处理或避免循环依赖:

  1. 使用 @Lazy 注解 @Lazy 注解通过延迟加载来打破循环。它对构造函数注入尤为有效。

    java 复制代码
    @Service
    public class ServiceA {
        private ServiceB serviceB;
        public ServiceA(@Lazy ServiceB serviceB) { // 此时注入的是代理对象
            this.serviceB = serviceB;
        }
    }
  2. 使用 Setter/字段注入替代构造器注入 Spring 默认只能解决单例模式下通过 Setter 方法或字段属性注入(@Autowired)形成的循环依赖 ,而无法解决构造器注入导致的循环依赖。因为构造器注入发生在实例化阶段,此时对象都还未创建,无法提前暴露引用。

  3. 使用 @PostConstruct 在其中一个 Bean 注入完成后,再通过注解方法设置依赖。

    java 复制代码
    @Service
    public class ServiceA {
        @Autowired
        private ServiceB serviceB;
        // 其他代码...
    }
    
    @Service
    public class ServiceB {
        private ServiceA serviceA;
        @Autowired
        public void setServiceA(ServiceA serviceA) {
            this.serviceA = serviceA;
        }
    }
  4. 实现 ApplicationContextAwareInitializingBean 这是一种更手动的方式,让 Bean 主动从 Spring 上下文中获取依赖。

    java 复制代码
    @Service
    public class ServiceA implements ApplicationContextAware, InitializingBean {
        private ApplicationContext context;
        private ServiceB serviceB;
        @Override
        public void setApplicationContext(ApplicationContext context) {
            this.context = context;
        }
        @Override
        public void afterPropertiesSet() {
            // 在所有属性设置完成后,再从容器中获取ServiceB
            this.serviceB = context.getBean(ServiceB.class);
        }
    }

⚠️ 注意事项与限制

Spring 的解决方案虽然强大,但并非万能,存在以下限制:

  • 必须是单例(Singleton)Bean原型(Prototype)作用域的 Bean 无法利用三级缓存解决循环依赖。
  • 不支持构造器循环依赖 :如果循环依赖是通过构造器注入(Constructor Injection)方式发生的,三级缓存也无法解决。
  • 设计警示 :虽然 Spring 提供了这些机制,但从设计角度来说,应用程序中出现循环依赖通常是不推荐的,它可能意味着代码的职责划分不够清晰。

💡 最佳实践 如果条件允许,优先考虑通过代码重构(如提取公共逻辑到第三个类、使用接口分离职责等)来消除循环依赖,这往往是最根本的解决方案。

💎 总结

Spring 主要通过三级缓存机制 自动解决单例 Bean 的 setter/字段注入 导致的循环依赖,其本质是空间换时间,通过提前暴露对象引用打破循环。

对于无法自动处理的场景(如构造器注入或需要更细粒度控制),可以使用 @Lazy@PostConstruct 或实现接口等编程方式辅助解决。

理解这些机制和限制,能帮助你在设计和调试时更有把握。但记住,一个良好架构的系统应尽量避免循环依赖。





🤔 参考 12

Spring 通过 三级缓存机制对象生命周期管理 巧妙解决了单例 Bean 的循环依赖问题,其核心设计兼顾了性能、线程安全和 AOP 代理兼容性。以下是具体实现原理和流程解析:


一、循环依赖的本质与场景 循环依赖指 两个或多个 Bean 相互引用形成闭环,例如:

  • 属性注入循环A → B → A
  • 构造器注入循环A 的构造器依赖 B,B 的构造器依赖 A

Spring 仅支持 单例作用域(Singleton) 的循环依赖解决,原型(Prototype)作用域的 Bean 无法处理。


二、三级缓存的核心设计 Spring 的三级缓存定义在 DefaultSingletonBeanRegistry 类中,各层级作用如下:

缓存层级 名称 存储内容 核心作用
一级缓存 singletonObjects 完全初始化的单例 Bean(包括依赖注入和 AOP 代理完成后的最终实例) 直接提供可用的最终 Bean,供外部获取
二级缓存 earlySingletonObjects 实例化但未完成初始化的 Bean(如构造函数执行完成,但属性注入或初始化方法未执行) 允许其他 Bean 在循环依赖中获取早期引用,打破循环等待
三级缓存 singletonFactories 存储生成早期引用的 ObjectFactory 对象 支持动态生成代理对象(如 AOP 场景),确保不同依赖链获取的代理对象引用一致

三、循环依赖的解决流程(以属性注入为例)A → B → A 为例,流程如下:

  1. 创建 Bean A

    • 实例化 A:通过反射调用构造函数生成原始对象。
    • 暴露早期引用 :将 ObjectFactory 存入三级缓存(singletonFactories),用于后续生成早期引用(可能是代理对象)。
    • 依赖注入 B:发现 A 依赖 B,触发 B 的创建流程。
  2. 创建 Bean B

    • 实例化 B:生成原始对象。
    • 暴露早期引用 :将 B 的 ObjectFactory 存入三级缓存。
    • 依赖注入 A:发现 B 依赖 A,尝试从缓存获取 A 的实例。
  3. 解决循环依赖

    • 从三级缓存获取 A 的工厂 :调用 getObject() 生成 A 的早期引用(可能为代理对象)。
    • 存入二级缓存 :将 A 的早期引用移至 earlySingletonObjects,供 B 注入。
    • 完成 B 的初始化:注入 A 的早期引用后,B 完成初始化并存入一级缓存。
  4. 完成 A 的初始化

    • 注入 B 的最终实例:从一级缓存获取已完成的 B,完成 A 的依赖注入。
    • A 存入一级缓存:A 的初始化完成后,移出二级缓存,最终存入一级缓存。

四、关键设计解析 1. 提前暴露半成品对象

  • 三级缓存的作用 :在 Bean 实例化后、属性注入前,将 ObjectFactory 暴露到三级缓存,允许其他 Bean 获取未完全初始化的对象。
  • 二级缓存的过渡:早期引用从三级缓存转移到二级缓存,避免污染一级缓存(存放完整 Bean)。
  1. AOP 代理兼容性
  • 动态代理生成 :若 Bean 需要 AOP 代理,singletonFactories 中的 ObjectFactory 会调用 getEarlyBeanReference() 生成代理对象,确保依赖注入的始终是代理实例。
  • 一致性保障:三级缓存通过工厂模式延迟生成代理,避免不同依赖链获取不同代理对象的问题。
  1. 线程安全与性能优化
  • 双重检查锁 :在 getSingleton() 方法中,通过 synchronizedvolatile 保证多线程环境下缓存的正确性。
  • 懒加载策略:仅在需要时生成早期引用,减少不必要的对象创建。

五、构造器注入的循环依赖问题 Spring 无法通过三级缓存解决构造器注入的循环依赖,原因如下:

  • 实例化即依赖注入:构造器注入在 Bean 实例化时就需要依赖对象,此时 Bean 尚未存入缓存。
  • 解决方案
    1. @Lazy 注解 :延迟加载依赖的 Bean,打破循环。

      java 复制代码
      @Service
      public class A {
          @Autowired
          public A(@Lazy B b) { ... }
      }
    2. CGLIB 代理 :通过 @Scope(proxyMode = TARGET_CLASS) 生成代理类,代理对象持有依赖的引用。

      java 复制代码
      @Service
      @Scope(proxyMode = TARGET_CLASS)
      public class B { ... }

六、设计意图与最佳实践

  1. 核心目标

    • 解耦依赖链:允许 Bean 在未完全初始化时被引用。
    • 兼容 AOP:确保代理对象在依赖注入时的一致性。
    • 性能优化:减少重复对象创建和锁竞争。
  2. 开发建议

    • 优先使用 Setter/字段注入:避免构造器注入导致的循环依赖。
    • 合理拆分模块:通过引入中间层(如 Facade 模式)打破循环。
    • 谨慎使用 @Lazy:过度使用可能掩盖设计缺陷。

总结 Spring 的三级缓存机制通过 分层存储 Bean 的不同生命周期状态,结合动态代理技术,实现了对单例 Bean 循环依赖的高效解决。其设计体现了对性能、灵活性和扩展性的深度权衡,是 Spring IoC 容器的核心优势之一。





🤔 参考 13

Spring 通过其独特的三级缓存机制有效解决了循环依赖问题,以下是核心原理及关键步骤:

一、核心前提与限制条件

  1. 仅适用于单例模式 :循环依赖的两个 Bean 必须均为单例(singleton),原型(prototype)作用域的 Bean 因不存入缓存而无法提前暴露引用[^4^][^5^]。

  2. 非构造函数注入:若依赖通过构造函数注入,由于实例化阶段需立即满足依赖关系,无法提前暴露半成品对象,导致死循环[^4^][^5^]。

  3. Setter/Field 注入方式:依赖需通过 Setter 方法或字段注入,允许延迟注入时机[^3^][^4^]。

二、三级缓存机制解析

  1. 一级缓存 singletonObjects

    • 存储内容:完全初始化完成的单例 Bean(已完成属性注入、初始化及 AOP 代理生成)[^1^][^4^]。
    • 作用:提供最终可用的 Bean 实例,避免重复初始化[^1^][^4^]。
  2. 二级缓存 earlySingletonObjects

    • 存储内容:早期暴露的未完成初始化的 Bean 引用(已实例化但未进行属性注入和初始化)[^1^][^4^]。
    • 作用:供循环依赖时快速获取半成品对象,打破初始化顺序的限制[^1^][^4^]。
  3. 三级缓存 singletonFactories

    • 存储内容 :生成 Bean 实例的工厂对象(ObjectFactory),而非直接存储 Bean[^1^][^4^]。
    • 核心作用 :动态决定是否创建 AOP 代理对象。若 Bean 需代理(如 @Transactional),则通过工厂生成代理而非原始对象[^1^][^4^]。

三、解决循环依赖的完整流程

以经典的A→B→A依赖链为例:

  1. 实例化与三级缓存注册

    • 当创建 Bean A 时,先实例化 A 的对象,并将其封装为 ObjectFactory 存入三级缓存[^1^][^4^]。此时暂不放入一/二级缓存。
  2. 处理依赖 Bean B

    • 在为 A 注入依赖 B 时,发现 B 未初始化,触发 B 的创建流程。同样将 B 的 ObjectFactory 存入三级缓存[^1^][^4^]。
  3. 检测到循环依赖

    • 当创建 B 的过程中试图注入 A 时,从三级缓存中获取 A 的 ObjectFactory,调用其 getObject() 方法生成早期引用[^1^][^4^]。
  4. 升级缓存层级

    • 将 A 的早期引用从三级缓存移至二级缓存 earlySingletonObjects,供 B 完成属性注入[^1^][^4^]。
  5. 完成初始化并最终放入一级缓存

    • B 完成属性注入后,将其完整实例放入一级缓存 singletonObjects[^1^][^4^]。
    • 随后继续完成 A 的初始化,最终也将 A 放入一级缓存[^1^][^4^]。

四、关键设计优势

  1. 双重延迟决策

    • 延迟点1:仅在被依赖时才从三级缓存生成早期引用[^1^][^4^]。
    • 延迟点2:动态判断是否需要创建 AOP 代理,避免不必要的代理对象生成[^1^][^4^]。
  2. 状态完整性保障

    • 早期引用已通过 populateBean() 完成属性注入,确保代理对象可安全使用属性值[^1^][^4^]。
  3. 资源高效利用

    • 无循环依赖时不生成代理对象,节省内存;有循环依赖时复用同一代理对象,避免多例冲突[^1^][^4^]。

五、无法解决的场景

  1. 构造器循环依赖:实例化阶段无法提前暴露半成品对象[^4^][^5^]。

  2. 原型作用域 Bean:每次获取新实例,无法通过缓存解决[^4^][^5^]。

  3. 特殊处理器干扰 :如 BeanPostProcessorBeanFactoryPostProcessor 参与依赖链,可能中断缓存机制[^4^][^5^]。

总的来说,Spring 的三级缓存机制通过分层存储 Bean 的不同状态(实例化 → 属性注入 → 初始化完成),结合工厂模式动态生成代理对象,巧妙解决了循环依赖问题。这一机制不仅保证了Bean生命周期的正确性,还兼容了 AOP 等高级特性,是 Spring IoC 容器的核心设计之一。





🤔 参考 14

Spring 通过 三级缓存机制 来解决 单例 Bean 的循环依赖 问题。以下是其核心原理和解决过程:


✅ 支持解决的循环依赖类型

  • 仅适用于单例作用域(Singleton)的 Bean
  • 只能解决通过 setter 方法注入的循环依赖
  • 不能解决构造器注入的循环依赖

🔁 什么是循环依赖?

当两个或多个 Bean 相互依赖,形成闭环时,就构成了循环依赖。例如:

java 复制代码
class A {
    private B b;
}

class B {
    private A a;
}

在创建 A 时需要注入 B,而创建 B 又需要注入 A,形成循环。


🧠 Spring 如何解决?

Spring 使用 三级缓存机制 来处理循环依赖:

  1. 一级缓存(singletonObjects) 存放已经完全初始化好的 Bean。

  2. 二级缓存(earlySingletonObjects) 存放提前暴露的"早期 Bean"(尚未完全初始化)。

  3. 三级缓存(singletonFactories) 存放 Bean 工厂(ObjectFactory),用于创建代理对象(如 AOP 代理)。


🔄 解决流程(以 A → B → A 为例)

  1. 开始创建 Bean A

    • 实例化 A(调用构造函数,但未填充属性、未初始化)。
    • 将 A 的工厂对象(ObjectFactory)放入三级缓存。
  2. 填充 A 的属性

    • 发现 A 依赖 B,于是开始创建 B。
  3. 开始创建 Bean B

    • 实例化 B,也放入三级缓存。
    • 填充 B 的属性时发现依赖 A。
  4. B 获取 A 的依赖

    • 从 A 的三级缓存中获取 A 的早期引用(可能是代理对象)。
    • 将 A 的早期引用移动到二级缓存,并从三级缓存中移除工厂。
  5. B 完成初始化

    • B 完成属性注入和初始化,放入一级缓存。
  6. 继续初始化 A

    • A 从一级缓存中获取已初始化的 B。
    • A 完成初始化,放入一级缓存。

🚫 为什么构造器注入无法解决?

构造器注入在实例化时就需要所有依赖对象,而此时依赖对象可能尚未创建,因此 Spring 无法提前暴露 Bean,导致循环依赖无法解决。


✅ 总结

Spring 解决循环依赖的关键在于:

  • 三级缓存机制 提前暴露早期 Bean。
  • ObjectFactory 支持代理对象的创建(AOP)。
  • 仅适用于单例、setter 注入的场景

Spring 的这一设计在保证灵活性的同时,有效地解决了常见的循环依赖问题。





🤔 参考 15

Spring 解决循环依赖的核心机制是通过三级缓存(Three-Level Cache)实现的,它允许在对象完全初始化之前提前暴露引用,从而打破环依赖关系。

具体实现原理如下:

  1. 三级缓存结构

    • 一级缓存(singletonObjects):存储完全初始化完成的单例对象
    • 二级缓存(earlySingletonObjects):存储提前暴露的、未完全初始化的单例对象
    • 三级缓存(singletonFactories):存储对象工厂,用于生成对象的早期引用
  2. 解决过程

    • 当创建对象A时,先实例化A(未设置属性),然后将A的工厂放入三级缓存
    • 当A需要依赖B时,开始创建B
    • B实例化后,需要依赖A,此时从三级缓存获取A的早期引用(通过工厂创建)
    • 将A的早期引用放入二级缓存,同时从三级缓存移除
    • B完成初始化,放入一级缓存
    • A拿到B的引用,完成初始化,放入一级缓存
  3. 适用范围

    • 仅适用于单例Bean
    • 构造器注入无法解决循环依赖(因为实例化前就需要依赖)
    • 字段注入和setter注入可以通过三级缓存解决

代码层面的核心逻辑在DefaultSingletonBeanRegistry中,关键方法包括:

  • getSingleton():从缓存获取或创建Bean
  • addSingletonFactory():添加对象工厂到三级缓存
  • doCreateBean():创建Bean实例并处理依赖

这种机制既保证了Bean的完整性,又解决了循环依赖问题,是Spring容器的重要特性之一。







Spring的三级缓存原理 笔记251008

相关推荐
白水先森3 小时前
Python 字符串与布尔值详解
java·服务器·前端
kennylee263 小时前
WMS系统全景解析:从业务流到系统智能的深度设计
java·交通物流
往事随风去3 小时前
惊!多线程编程竟成内存杀手:90%程序员不知道的OOM陷阱
java·后端
vadvascascass3 小时前
平滑加权轮询负载均衡的底层逻辑
java·算法·负载均衡
玩毛线的包子3 小时前
Android Gradle学习(十二)- ASM对于字节码的增删改查
java
晓宜4 小时前
Java25 新特性介绍
java·python·算法
Seven974 小时前
SpringIOC、DI及Bean线程安全面试题解析
java
TitosZhang4 小时前
BIO、NIO、AIO详解
java·redis·nio
Arva .4 小时前
Spring Boot 配置文件
java·spring boot·后端