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
相关推荐
liu_bees10 小时前
Jenkins 中修改 admin 账号密码的正确位置与方法
java·运维·tomcat·jenkins
明洞日记10 小时前
【设计模式手册011】享元模式 - 共享细粒度对象的高效之道
java·设计模式·享元模式
G皮T10 小时前
【Java】Java 运行时数据区域(一):名词概念
java·jvm·runtime·运行时·运行时数据区域
z***y86210 小时前
Java数据挖掘开发
java·开发语言·数据挖掘
鱼锦0.010 小时前
基于spring+vue把图片文件上传至阿里云oss容器并回显
java·vue.js·spring
從南走到北10 小时前
JAVA国际版同城跑腿源码快递代取帮买帮送同城服务源码支持Android+IOS+H5
android·java·ios·微信小程序
q***098010 小时前
Spring Boot 2.7.x 至 2.7.18 及更旧的版本,漏洞说明
java·spring boot·后端
q***146411 小时前
oracle 12c查看执行过的sql及当前正在执行的sql
java·sql·oracle
q***563811 小时前
Spring Boot 中 RabbitMQ 的使用
spring boot·rabbitmq·java-rabbitmq