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

发布于:2025-08-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

Spring Boot @Configuration@Component 笔记

1️⃣ 基本概念

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

2️⃣ 单例与代理机制

Spring 中的 Bean 默认是单例的

  1. 直接注入 Bean(@Autowired

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

    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 代码

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") 或开启覆盖:

    spring.main.allow-bean-definition-overriding=true
    

网站公告

今日签到

点亮在社区的每一天
去签到