Spring Boot `@Configuration` 与 `@Component` 笔记

Spring Boot @Configuration 与 @Component 笔记

1️⃣ 基本概念

注解 作用 是否有代理 适用场景
@Component 标记普通组件,将类交给 Spring 容器管理 ❌ 没有 CGLIB 代理 普通 Bean,工具类、过滤器、监听器等
@Configuration 标记配置类,用来声明 @Bean ✅ 有 CGLIB 代理 声明 Bean,保证同一个 @Bean 方法多次调用返回同一个对象

2️⃣ 单例与代理机制

Spring 中的 Bean 默认是单例的:

  1. 直接注入 Bean(@Autowired)

    java 复制代码
    @Autowired
    private MyService myServiceA;
    
    @Autowired
    private MyService myServiceB;
    • 容器中只有一份 MyService 实例
    • 无论注入多少次,拿到的都是同一个对象
    • 和配置类是否用代理无关
  2. 通过配置类方法调用 Bean

    java 复制代码
    MyService s1 = configA.myServiceA();
    MyService s2 = configA.myServiceA();
    
    MyService s3 = configB.myServiceB();
    MyService s4 = configB.myServiceB();
    • @Configuration 的类被 Spring 生成了 CGLIB 代理

      • 每次调用 myServiceA() → 代理先检查容器
      • 容器已有实例就直接返回 → s1 == s2
    • @Component 的类没有代理

      • 每次调用 myServiceB() → 都是 new 一个新对象 → s3 != s4

3️⃣ Demo 代码

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

// 业务类
@Service
public class MyService {
}

// 配置类 A
@Configuration
public class ConfigA {

    @Bean
    public MyService myServiceA() {
        return new MyService();
    }
}

// 配置类 B,用 Component 替代 Configuration
@Component
public class ConfigB {

    @Bean
    public MyService myServiceB() {
        return new MyService();
    }
}

// 测试 Runner
@Component
public class TestRunner implements CommandLineRunner {

    @Autowired
    private ConfigA configA;

    @Autowired
    private ConfigB configB;

    @Autowired
    private MyService myServiceA;

    @Autowired
    private MyService myServiceB;

    @Override
    public void run(String... args) throws Exception {

        System.out.println("从容器拿到 myServiceA: " + myServiceA);
        System.out.println("从容器拿到 myServiceB: " + myServiceB);

        // 调用配置类方法
        MyService s1 = configA.myServiceA();
        MyService s2 = configA.myServiceA();

        MyService s3 = configB.myServiceB();
        MyService s4 = configB.myServiceB();

        System.out.println("ConfigA.myServiceA() 两次调用是否同一对象: " + (s1 == s2)); // true
        System.out.println("ConfigB.myServiceB() 两次调用是否同一对象: " + (s3 == s4)); // false
    }
}

4️⃣ 结论

  1. 注入 Bean → 拿到的都是容器里的单例

  2. 调用配置类方法

    • @Configuration → 通过代理保证单例
    • @Component → 没有代理,每次调用返回新对象
  3. 如果想保证通过方法调用也返回单例,必须使用 @Configuration


5️⃣ 小贴士

  • 不要把 @Configuration 写成 @Component,否则通过配置类方法调用 Bean 可能产生多个对象

  • Bean 名称冲突可以通过 @Bean("myBeanName") 或开启覆盖:

    yaml 复制代码
    spring.main.allow-bean-definition-overriding=true
相关推荐
AI你一生一世1 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
云-缩影1 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
瑞克-桑切斯2 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
前进的李工3 小时前
Agent 学习笔记 05:系统提示词应该怎么写
笔记·学习·prompt·agent·个人开发·few-shot·ai应用开发
AI开源软件3 小时前
SpringBoot3+Vue3 财务管理系统如何设计:从业务单据到总账的模块划分
spring boot·vue3·领域建模·会计凭证·ruoyi office·业财协同·财务系统设计
蒸蒸yyyyzwd5 小时前
后端学习笔记三个知识
笔记·八股
海的透彻6 小时前
dockerfile如何快速构建容器(java服务)
java·开发语言
code2cat6 小时前
Java工具篇之Gradle:用任务图组织构建与交付
java·开发语言·单元测试·gradle
后台模板学习7 小时前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
w_zero_one7 小时前
算法--构造
java·数据结构·算法