You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中getBean()是否共享Bean实例?循环依赖代码疑问

关于Spring Setter注入解决循环依赖的实例复用疑问

我编写了一个使用setter-based dependency injection解决循环依赖的程序,但输出结果有些令人困惑。

代码

package com.spring.core.di;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
@ComponentScan("com.spring.core.di")
class CoolAppConfig {

}

@Component
class DependencyA {
    DependencyB dependencyB;

    public DependencyA() {
        System.out.println("I am constructor of Dependency A");
    }
    
    @Autowired
    public void setDependencyB(@Qualifier("dependencyB") DependencyB dependencyB) {
        System.out.println("I am setDependencyB !");
        this.dependencyB = dependencyB;
    }

    public void say() {
        this.dependencyB.who();
    }

    public void who() {
        System.out.println("I am Dependency A");
    }
}

@Component
class DependencyB {
    DependencyA dependencyA;

    public DependencyB() {
        System.out.println("I am constructor of Dependency B");
    }

    @Autowired
    public void setDependencyA(@Qualifier("dependencyA") DependencyA dependencyA) {
        System.out.println("I am setDependencyA !");
        this.dependencyA = dependencyA;
    }

    public void say() {
        this.dependencyA.who();
    }

    public void who() {
        System.out.println("I am dependency B");
    }
}

public class CircularDependencyIssue {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CoolAppConfig.class);

        DependencyA dependencyA = applicationContext.getBean(DependencyA.class);
        DependencyB dependencyB = applicationContext.getBean(DependencyB.class);

        dependencyA.say();
        dependencyB.say();
    }
}

输出

I am constructor of Dependency A
I am constructor of Dependency B
I am setDependencyA !
I am setDependencyB !
I am dependency B
I am Dependency A

我的疑问是:在main方法中获取DependencyB的Bean时,为何DependencyB的构造器没有被再次调用?输出中“I am constructor of Dependency B”仅出现一次,但按预期应打印两次。这是否意味着解决DependencyA依赖时创建的DependencyB实例与后续获取的DependencyB Bean是同一个?同时想确认getBean()方法是否会为不同的Bean请求共享实例,以及整个流程的运作原理。


解答

核心原因:Spring默认Bean作用域是单例(Singleton)

Spring容器中,默认所有用@Component(或同类注解)标记的Bean都是单例作用域,容器只会为每个Bean创建一个实例,并将这个实例缓存起来,后续所有getBean()请求都会直接返回缓存中的对象,不会重新调用构造器创建新实例。这就是你看到构造器只执行一次的根本原因。

循环依赖的完整解决流程

  1. 容器启动后,扫描到DependencyA和DependencyB,开始初始化Bean:
    • 先创建DependencyA的未完全初始化实例(仅调用构造器),并将这个实例放入Spring的「早期单例缓存」中。
    • 接着处理DependencyA的setter注入,发现需要DependencyB,于是转向创建DependencyB。
  2. 创建DependencyB的实例(调用构造器),放入缓存,接着处理它的setter注入,发现需要DependencyA:
    • 此时直接从「早期单例缓存」中取出那个未完全初始化的DependencyA实例,注入给DependencyB,DependencyB完成初始化。
  3. 回到DependencyA的setter注入,将已经完全初始化的DependencyB实例注入,DependencyA也完成初始化。
  4. 最终两个Bean都被存入Spring的单例缓存池,后续任何getBean()请求都直接取缓存里的实例。

getBean()的行为规则

  • 单例作用域(默认):每次调用getBean()都会返回同一个缓存中的实例,构造器仅在Bean第一次初始化时执行一次。
  • 原型作用域(@Scope("prototype")):每次调用getBean()都会创建新的实例,构造器会重复执行,但注意:原型作用域下Spring无法自动解决循环依赖,会抛出异常,这也是Spring只在单例作用域下支持循环依赖解决的原因之一。

内容的提问来源于stack exchange,提问作者Neeraj-Kumar-Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:42:02