对比 @Configuration(Full) vs @Component(Lite)

核心现象:同一个类里面,@Bean方法互相调用,Full拿单例,Lite新建对象

1. 两个简单Bean

java 复制代码
public class ABean {
}

public class BBean {
    public ABean aBean;
    public BBean(ABean aBean) {
        this.aBean = aBean;
    }
}

2. Full模式(@Configuration)

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // Full模式,CGLIB代理
public class FullConfig {

    @Bean
    public ABean aBean() {
        System.out.println("Full -> 创建ABean");
        return new ABean();
    }

    @Bean
    public BBean bBean() {
        // 在配置类内部调用 aBean()
        ABean a = aBean();
        return new BBean(a);
    }
}

3. Lite模式(@Component + @Bean)

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component // Lite模式,无CGLIB代理
public class LiteConfig {

    @Bean
    public ABean aBean() {
        System.out.println("Lite -> 创建ABean");
        return new ABean();
    }

    @Bean
    public BBean bBean() {
        // 普通java方法调用!
        ABean a = aBean();
        return new BBean(a);
    }
}

4. 启动类 & 测试

java 复制代码
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class BeanDemoApp implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(BeanDemoApp.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        ApplicationContext ctx = BeanDemoApp.this;

        // FullConfig
        BBean fullB = ctx.getBean("bBean", BBean.class);
        ABean fullA = ctx.getBean("aBean", ABean.class);
        System.out.println("Full模式:bBean里面的aBean 和容器aBean是否同一个:" + (fullB.aBean == fullA));

        // LiteConfig
        BBean liteB = ctx.getBean("liteConfig.bBean", BBean.class);
        ABean liteA = ctx.getBean("liteConfig.aBean", ABean.class);
        System.out.println("Lite模式:bBean里面的aBean 和容器aBean是否同一个:" + (liteB.aBean == liteA));
    }
}

控制台输出结果

复制代码
Full -> 创建ABean
Full模式:bBean里面的aBean 和容器aBean是否同一个:true

Lite -> 创建ABean
Lite -> 创建ABean
Lite模式:bBean里面的aBean 和容器aBean是否同一个:false

解读

  1. Full(@Configuration)

    bBean()里调用aBean(),不是直接执行方法,而是被代理拦截,从Spring容器返回已存在的单例,只实例化一次。

  2. Lite(@Component + @Bean)

    没有代理,bBean()调用aBean()就是普通Java方法执行,new了两次ABean :一次是bBean构造用的,一次是Spring扫描@Bean注册的,两个对象地址不一样。

⚠️ 注意:如果不通过内部方法调用 ,只是单独定义一堆互不引用的@Bean,Lite模式看起来一切正常,坑藏在内部互相调用场景。

补充:Spring 6 / SpringBoot3 新变化

Spring6 引入 @Configuration(proxyBeanMethods = false)

java 复制代码
@Configuration(proxyBeanMethods = false)
public class Config {
    @Bean
    ...
}

这个也是Lite模式 !

虽然写了@Configuration,但是关闭代理,内部方法调用同样会新建对象。

现在SpringBoot很多自动配置都用这个,好处:不需要CGLIB,启动更快,代价就是不能内部调用@Bean方法。


相关推荐
软件开发技术深度爱好者1 小时前
ndexTTS-2和 CosyVoice之TTS(Text-to-Speech)使用python示例
开发语言·python
RuoyiOffice1 小时前
达梦 DM8、openGauss、人大金仓怎么选?RuoYi Office 国产数据库适配与验收指南
java·数据库·spring boot·架构·信创
修船大队长1 小时前
KL15 高电平请求 FullCom:AUTOSAR 项目调用流程
android·java·服务器
棉猴1 小时前
玩游戏学Python12-布尔型与条件语句
java·前端·python·玩游戏·游戏编程·pgzero
晚枫歌F1 小时前
dpdk实现arp协议,icmp协议-----架构优化
开发语言·javascript·架构
java_nnnn2 小时前
Spring Web MVC 入门:从一个 URL 到 Java 后端完整处理请求
java·前端·spring boot·spring
Mikko72 小时前
fastjson 1.2.84 能在 JDK 7 上跑吗?实测:编译能过,一启动就 UnsupportedClassVersionError
java·后端·jdk·fastjson·安全漏洞
G018_star sky♬2 小时前
为什么java新建一个对象,需要obj o = new obj()这种写法
java·开发语言
星恒讯工业路由器2 小时前
工业网络设备选型:导轨式、机架式与车载式的部署差异
开发语言·网络·数据库·php·工业路由器·导轨式·车载式