开发者

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

开发者 https://www.devze.com 2025-10-18 10:20 出处:网络 作者: 渣哥
目录一、有哪些主流方式?1) 组件扫描:@Component 家族(含 @Service/@Repository/@Controller)2) Java 配置:@Configuration + @Bean3) FactoryBean<T>:自定义工厂4) @Import 家族:装配拼装器5) XML(<
目录
  • 一、有哪些主流方式?
    • 1) 组件扫描:@Component 家族(含 @Service/@Repository/@Controller)
    • 2) Java 配置:@Configuration + @Bean
    • 3) FactoryBean<T>:自定义工厂
    • 4) @Import 家族:装配拼装器
    • 5) XML(<bean/>)
    • 6) 运行时注册:BeanDefinitionRegistry / GenericApplicationContext#registerBean
  • 二、怎么选?——一张可落地的决策清单
    • 三、组合拳:把方式与“条件/环境/范围/生命周期”拼起来
      • 四、常见踩坑与规避
        • 五、最佳实践清单(可直接落地)
          • 写在最后

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

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

            一、有哪些主流方式?

            1) 组件扫描:@Component 家族(含 @Service/@Repository/@C编程客栈ontroller)

            怎么用

            @Component
            public class OrderService { }
            

            特点

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

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

            2) Java 配置:@Configuration + @Bean

            怎么用

            @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>:自定义工厂

            怎么用

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

            特点

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

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

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

            怎么用

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

            特点

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

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

            5) XML(<bean/>)

            特点

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

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

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

            怎么用

            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

            • 池/连接类务必正确清理。

            配置类性能@Configuratjavascription(proxyBeanMethods = false)

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

            四、常见踩坑与规避

            1. 同名/同类型冲突
            python
            • 现象: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(绕过代理)。
            • 规则:所有需要切面的对象都由容器生产

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

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

            写在最后

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

            以上就是Spring创建Bean的多种方式对比与最佳实践的详细内容,更多关于Spring创建Bean方式的资料请关注编程客栈(www.devze.com)其它相关文章!

            0

            精彩评论

            暂无评论...
            验证码 换一张
            取 消

            关注公众号