面试官:说说 @Configuration 和 @Component 的区别

一句话概括 就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

理解 调用 @Configuration 类中的 @Bean 注解的方法,返回的是同一个实例;而调用@Component 类中的 @Bean 注解的方法,返回的是一个新的实例。

注意: 上面说的调用,而不是从 Spring 容器中获取! 见最下面的 示例一 及 示例二

下面看看实现的细节:

@Configuration 注解:

Java 复制代码
@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Configuration {  
    String value() default "";  
}

从定义来看,@Configuration 注解本质上还是 @Component,因此 <context:component-scan/> 或者 @ComponentScan 都能处理 @Configuration 注解的类。

@Configuration 标记的类必须符合下面的要求:

  • 配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib动态代理);
  • 配置类不能是 final 类(无法动态代理);
  • 配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类;
  • 配置类必须是非本地的(即不能在方法中声明,不能是 private)
  • 任何嵌套配置类都必须声明为 static;
  • @Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。

@Bean 注解方法执行策略

先给一个简单的示例代码:

Java 复制代码
@Configuration  
public class MyBeanConfig {  
  
    @Bean  
    public Country country(){  
        return new Country();  
    }  
  
    @Bean  
    public UserInfo userInfo(){  
        return new UserInfo(country());  
    }  
}

相信大多数人第一次看到上面 userInfo() 中调用 country() 时,会认为这里的 Country 和 上面 @Bean 方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:

  • @Resource private Country country;

实际上不需要这么做(后面会给出需要这样做的场景),直接调用 country() 方法返回的是同一个实例。

@Component 注解

@Component 注解并没有通过 cglib 来代理 @Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country。

Java 复制代码
@Component  
public class MyBeanConfig {    
    @Bean  
    public Country country(){  
        return new Country();  
    }  
  
    @Bean  
    public UserInfo userInfo(){  
        return new UserInfo(country());  
    }
}

有些特殊情况下,我们不希望 MyBeanConfig 被代理(代理后会变成 WebMvcConfigEnhancerBySpringCGLIB8bef3235293 )时,就需要使用 @Component,这种情况下,上面的写法就需要改成下面这样:

Java 复制代码
@Component  
public class MyBeanConfig {  
  
    @Autowired  
    private Country country;  
  
    @Bean  
    public Country country(){  
        return new Country();  
    }  
  
    @Bean  
    public UserInfo userInfo(){  
        return new UserInfo(country);  
    }  
}

这种方式可以保证使用的是同一个 Country 实例。

示例一:调用 @Configuration 类中的 @Bean 注解的方法,返回的是同一个实例

第一个 bean 类
Java 复制代码
package com.xl.test.logtest.utils;

public class Child {
    private String name = "the child";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
第二个 bean 类
Java 复制代码
package com.xl.test.logtest.utils;

public class Woman {

    private String name = "the woman";

    private Child child;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Child getChild() {
        return child;
    }

    public void setChild(Child child) {
        this.child = child;
    }
}
@Configuration 类
Java 复制代码
package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
//@Component
public class Human {

    @Bean
    public Woman getWomanBean() {
        Woman woman = new Woman();
        woman.setChild(getChildBean()); // 直接调用@Bean注解的方法方法getChildBean()
        return woman;
    }

    @Bean
    public Child getChildBean() {
        return new Child();
    }
}

测试类 I

本测试类为一个配置类,这样启动项目时就可以看到测试效果,更加快捷;也可以使用其他方式测试见下面的测试类 II

Java 复制代码
package com.xl.test.logtest.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Man {

    @Autowired
    public Man(Woman wn, Child child) {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(wn.getChild() == child ? "是同一个对象" : "不是同一个对象");
    }
}

启动项目,查看输出结果

测试类 II

Java 复制代码
package com.xl.test.logtest.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xl.test.logtest.utils.Child;
import com.xl.test.logtest.utils.Woman;

@RestController
public class LogTestController {
    @Autowired
    Woman woman ;

    @Autowired
    Child child;

    @GetMapping("/log")
    public String log() {
        return woman.getChild() == child ? "是同一个对象":"不是同一个对象";
    }
}

浏览器访问项目,查看结果;输入localhost:8080/log

示例二:调用 @Component 类中的 @Bean 注解的方法,返回的是一个新的实例。

只需要将测试代码中的 @Configuration 改为 @Component 即可!其他的均不变

Java 复制代码
package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Configuration
@Component
public class Human {

    @Bean
    public Woman getWomanBean() {
        Woman woman = new Woman();
        woman.setChild(getChildBean()); // 直接调用 @Bean 注解的方法方法getChildBean()
        return woman;
    }

    @Bean
    public Child getChildBean() {
        return new Child();
    }
}

测试结果,浏览器同样返回"不是同一个对象":

控制台和浏览器响应结果,均符合预期。

相关推荐
Zane199413 小时前
调用了 async 函数却没执行?一文讲透协程、event loop 与 await
后端·python
Zane199413 小时前
HashMap 为什么要在长度16、容量必须是2的幂这些细节上较劲
java·后端
掘金者阿豪14 小时前
若依启动突然报 Redis MISCONF?一次从应用报错到磁盘爆满的完整排查记录
后端
boooooooom15 小时前
尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
前端·后端·llm
2601_9638701816 小时前
【计算机毕业设计】基于Vue+Spring Boot的助农电商系统的设计与实现
spring boot·后端·课程设计
sun༒16 小时前
Spring @Scheduled 定时任务详解:Cron表达式、执行顺序、优先级与并行调度
java·后端·spring
yuhaiqiang16 小时前
从这两件事就能看出 vibecoding 距离专业作品差距有多大?AI 能抹平技术,但抹不平品味 !
前端·后端·程序员
野生技术架构师17 小时前
Spring Boot 实现数据脱敏:自定义注解 + Jackson 序列化器
java·spring boot·后端
明月_清风18 小时前
vLLM 深度实战:2026 年生产级 LLM 推理引擎完全指南
前端·后端·ai编程