Skip to content

@Bean 和@Component 有什么区别? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

@Bean 和 @Component 都能把对象注册进容器,区别在**"谁负责声明"和"怎么创建"**:

  • @Component:标在类上,Spring 调用它的构造器创建,要求你能修改类源码。业务组件的标准姿势;
  • @Bean:标在配置类的方法上,创建逻辑由你手写,方法返回什么容器就管什么。适合:
    1. 第三方库对象:HttpClient、Redisson 客户端、SDK 的 XxxClient,源码不是你的,没法加注解;
    2. 需要复杂初始化:要读配置、设置拦截器、连接池参数的组装过程;
    3. 同一类多实例:不同配置产出多个 Bean(主从数据源)。

一个容易被追问的细节:@Bean 方法之间的互调(配置类被 CGLIB 增强)返回的是容器里同一个实例,不是新对象——这是保证配置类内组装一致性的机制。

架构师视角的分层逻辑:@Component 是"我的代码交给容器",@Bean 是"别人的代码我替容器组装"。进一步讲,公司级starter 里清一色 @Bean + @ConditionalOnXxx——平台团队把中间件接入的复杂度封装掉,业务方引依赖即用。这个"注解给业务、方法给平台"的分工,就是我们控制集成成本的核心手段:中间件升级改 starter 一处,几十个业务仓库零改动。

🏗 系统架构师 ​

  • @Component(含派生注解):类级声明,「自我描述」的组件,靠类路径扫描发现;
  • @Bean:方法级声明,返回值入容器,方法名默认 Bean 名,方法参数即依赖——用于第三方类型(无法加注解)与需要编程逻辑构造的对象(按环境 new 不同实现、构造参数复杂)。

架构层面的差异:

  1. @Bean 所在的配置类是装配层,业务类是能力层——分层清晰时,看一个 Config 类就能看清模块的全部外部集成面;
  2. proxyBeanMethods:@Configuration 默认 true,方法互调走容器保证单例;false(lite 模式)去掉 CGLIB 代理换启动性能,Boot 内部大量使用——这是启动优化时容易被忽略的开关;
  3. AOT/Native 下 @Bean 方法在构建期追踪执行,过于动态的编程式逻辑受限——做 GraalVM 支持时这是改造重点。

💻 全栈开发专家 ​

两者都能注册 Bean,差别在使用场景:

  • 来源:@Component 标在自家类上靠扫描;@Bean 标在配置类方法上,返回值即 Bean
  • 第三方类:jar 包里的类加不了 @Component,只能 @Bean 手工装——我的第一判断军规:改不了的类用 @Bean
  • 创建逻辑:@Bean 是纯 Java 代码,可以条件判断、多步构建、按环境返回不同实现;@Component 只能反射调构造器
  • 生命周期:@Bean 直接声明 initMethod/destroyMethod,还自动推断 close 方法
  • 代理模式:@Bean 所在配置类有 proxyBeanMethods 属性,控制方法互调是否走容器,Boot 自动配置大量利用它提速
java
@Configuration(proxyBeanMethods = false)   // lite 模式,启动更快
public class HttpClientConfig {
    @Bean(destroyMethod = "close")
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(Duration.ofSeconds(2))
                .build();
    }
}

面试要的点:自家类用 @Component、第三方和复杂构造用 @Bean,再补一句 proxyBeanMethods=false 的提速细节,就是有实操的味道。