Spring 创建 Bean 的多种方式对比与最佳实践

原文来自于:zha-ge.cn/java/124

Spring 创建 Bean 的多种方式对比与最佳实践

第一次接手老项目,我最懵的一件事是:同一个项目里,创建 Bean 的姿势能有五六种 ------@Component@BeanFactoryBean@Import、XML、甚至运行时注册。到底选谁?标准究竟是什么?

下面把常见方式逐一拆开,顺手给你一张"怎么选"的脑图。


一、有哪些主流方式?

1) 组件扫描:@Component 家族(含 @Service/@Repository/@Controller

怎么用

java 复制代码
@Component
public class OrderService { }

特点

  • 简单、直观、与分层语义强绑定(@Service 等)。
  • 适合常规业务类;依赖通过构造器注入最佳。
  • 与 AOP、校验、事务天然契合。

何时选:90% 的业务类、无外部构造复杂度的 Bean。


2) Java 配置:@Configuration + @Bean

怎么用

java 复制代码
@Configuration
public class AppConfig {
  @Bean
  public IdGenerator idGenerator() { return new SnowflakeIdGenerator(...); }
}

特点

  • 显式声明生命周期、构造参数、工厂方法。
  • 可精细控制作用域、init/destroy@Conditional@Profile 等。
  • 注意@Configuration 默认 proxyBeanMethods = true(Full 模式,会 CGLIB 代理确保单例跨 @Bean 调用);在不需要跨方法引用保障的场景可设 false(Lite 模式)提升启动性能。

何时选

  • 需要精细构造/第三方库对象(DataSource、ObjectMapper、ThreadPool、KafkaClient...)。
  • 需要搭配条件装配/环境隔离时。

3) FactoryBean<T>:自定义工厂

怎么用

java 复制代码
@Component
public class ClientFactoryBean implements FactoryBean<Client> {
  public Client getObject() { return new Client(config()); }
  public Class<?> getObjectType() { return Client.class; }
}

特点

  • Bean 的"生产逻辑"可编程化,适合复杂/延迟/池化创建。
  • 获取"工厂本身"用:&clientFactoryBean
  • 便于封装复杂 SDK 实例化、动态代理实例、框架级 Bean。

何时选:构造过程复杂、需要"拿结果不是拿工厂"的场景。


4) @Import 家族:装配拼装器

怎么用

  • 直接导入配置类:@Import(AppConfig.class)
  • 选择器:ImportSelector(按条件返回类名集合)
  • 低层注册:ImportBeanDefinitionRegistrar(手动注册 BeanDefinition

特点

  • 适合模块化配置/Starter:把一组 Bean 一键装配。
  • @Conditional 组合,实现"自动装配"的开/关。

何时选框架/组件开发、Starter、按类路径/环境动态装配。


5) XML(<bean/>

特点

  • 历史包袱/遗留系统常见;与 JavaConfig 可并存。
  • 在强合规/模板化平台仍有价值。

何时选:遗留项目、平台强约束、需运行时热替换 XML 的场景。


6) 运行时注册:BeanDefinitionRegistry / GenericApplicationContext#registerBean

怎么用

java 复制代码
context.registerBean("userRepo", UserRepo.class, () -> new UserRepo(ds));

特点

  • 最灵活:可在运行中按条件装配/卸载。
  • 常用于框架扩展、动态多租户/插件化。

何时选框架层、插件/脚手架、动态场景。


二、怎么选?------一张可落地的决策清单

  1. 普通业务类@Component(或语义化的 @Service/@Repository),配合构造器注入
  2. 第三方对象/需要精细控制 (连接池、客户端、线程池)→ @Configuration + @Bean
  3. 创建逻辑复杂/需要返回"产品而非工厂"FactoryBean
  4. 模块化/Starter/按条件成批装配@ImportImportSelector/Registrar)+ @Conditional
  5. 遗留或平台要求 → XML。
  6. 动态注册/插件化 → 运行时注册 API。

高频口诀:"业组件、库配 Bean、难用工厂、批量用 Import、老活交 XML、动态上 Registry。"


三、组合拳:把方式与"条件/环境/范围/生命周期"拼起来

  • 条件装配@Conditional / @Profile

    • 例:仅在 prod 激活某个 @Bean;当类路径存在某依赖再装配。
  • 作用域@Scope("singleton"|"prototype"|"request"|"session")

    • 单例配 @Bean/@Component;原型慎用(生命周期管理在你)。
  • 懒加载@Lazy

    • 对重量级 Bean 延迟创建,缩短冷启动。
  • 优先/歧义@Primary / @Qualifier("xxx")

    • 多实现注入时避免"多候选"异常。
  • 生命周期initMethod/destroyMethod@PostConstruct/@PreDestroy

    • 池/连接类务必正确清理。
  • 配置类性能@Configuration(proxyBeanMethods = false)

    • @Bean 之间不相互调用,设 false 提升启动性能。

四、常见踩坑与规避

  1. 同名/同类型冲突
  • 现象:NoUniqueBeanDefinitionException
  • 解法:约定 Bean 命名;@Qualifier 精确注入;需要默认实现时加 @Primary
  1. 原型 Bean 生命周期失控
  • 容器只负责创建,不托管销毁;注入到单例里极易内存/状态泄漏。
  • 方案:ObjectProvider/Provider 延迟获取,或改成无状态。
  1. @Configuration 误用导致"重复实例化"
  • 在 Full 模式下通过代理保证同一 @Bean 单例复用;
  • proxyBeanMethods=false 时跨方法互调会各自新建,需谨慎。
  1. FactoryBean 取到的是"产品不是工厂"
  • 想拿工厂本身要用 &beanName。这点面试常考。
  1. 条件/环境不生效
  • 激活 profile 写错;@ConditionalOnClass 等判断失败。
  • 启动参数/环境变量要对齐:spring.profiles.active=prod
  1. AOP/事务不起效
  • Bean 未交给容器管理、或在 @Bean 方法里手动 new(绕过代理)。
  • 规则:所有需要切面的对象都由容器生产

五、最佳实践清单(可直接落地)

  • 优先用构造器注入 ,配合 final 字段保证不可变与可测试性。
  • 业务类用 @Component 家族第三方对象用 @Bean,职责清晰。
  • Starter/框架层用 @Import + Conditional,形成模块化装配
  • 对重量 Bean 加 @Lazy,对多实现用 @Qualifier,给默认实现标 @Primary
  • 配置类若无跨 @Bean 互调,开启 @Configuration(proxyBeanMethods = false) 提升启动性能。
  • 需要复杂构造/代理产物,优先考虑 FactoryBean 封装。
  • 原型 Bean 慎用,确需动态实例用 ObjectProvider 拉取。
  • 生命周期要闭环:连接/线程池显式 destroyMethod,或实现 DisposableBean

写在最后|一句话带走这篇文

把"谁创建、何时创建、在哪创建、如何销毁"说清楚,就是 Bean 策略的全部。 业务常态化用 @Component;第三方与精细控制用 @Bean;复杂构造上 FactoryBean;模块化上 @Import;其余按场景增量选择。 选对方式,系统启动快、结构清晰、扩展成本更低。

相关推荐
SanOrintea19 小时前
electron中进程线程之间通信方式
服务器·javascript·electron
Copper peas19 小时前
axios使用过程
前端·javascript·vue.js
云鹤_19 小时前
【Amis源码阅读】如何将json配置渲染成页面?
前端·低代码
逛逛GitHub19 小时前
飞书多维表格 + 即梦 4.0,打造你的 AI 生图游乐场。
前端·github
行走在顶尖19 小时前
Vue3 基础笔记
前端
guoss19 小时前
实现渐变背景叠加渐变圆角边框
前端
枫,为落叶19 小时前
【vue】导出excel
前端·vue.js·excel
转转技术团队19 小时前
当 AI 走进前端开发:代理插件的全流程开发实践
前端·javascript·ai编程
慧一居士19 小时前
Quill 富文本编辑器 功能介绍,使用场景说明,使用示例演示
前端