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

相关推荐
咩咩啃树皮7 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚8 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白9 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司10 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地11 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415911 小时前
TCP超时重传机制是为了解决什么问题?
java
莫逸风13 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠13 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z1234567898614 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112314 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python