Spring創(chuàng)建Bean的多種方式對比與最佳實踐
第一次接手老項目,我最懵的一件事是:同一個項目里,創(chuàng)建 Bean 的姿勢能有五六種——@Component、@Bean、FactoryBean、@Import、XML、甚至運行時注冊。到底選誰?標(biāo)準究竟是什么?
下面把常見方式逐一拆開,順手給你一張“怎么選”的腦圖。
一、有哪些主流方式?
1) 組件掃描:@Component 家族(含 @Service/@Repository/@Controller)
怎么用
@Component
public class OrderService { }
特點
- 簡單、直觀、與分層語義強綁定(
@Service等)。 - 適合常規(guī)業(yè)務(wù)類;依賴通過構(gòu)造器注入最佳。
- 與 AOP、校驗、事務(wù)天然契合。
何時選:90% 的業(yè)務(wù)類、無外部構(gòu)造復(fù)雜度的 Bean。
2) Java 配置:@Configuration + @Bean
怎么用
@Configuration
public class AppConfig {
@Bean
public IdGenerator idGenerator() { return new SnowflakeIdGenerator(...); }
}
特點
- 顯式聲明生命周期、構(gòu)造參數(shù)、工廠方法。
- 可精細控制作用域、
init/destroy、@Conditional、@Profile等。 - 注意:
@Configuration默認proxyBeanMethods = true(Full 模式,會 CGLIB 代理確保單例跨 @Bean 調(diào)用);在不需要跨方法引用保障的場景可設(shè)false(Lite 模式)提升啟動性能。
何時選:
- 需要精細構(gòu)造/第三方庫對象(DataSource、ObjectMapper、ThreadPool、KafkaClient…)。
- 需要搭配條件裝配/環(huán)境隔離時。
3) FactoryBean<T>:自定義工廠
怎么用
@Component
public class ClientFactoryBean implements FactoryBean<Client> {
public Client getObject() { return new Client(config()); }
public Class<?> getObjectType() { return Client.class; }
}
特點
- Bean 的“生產(chǎn)邏輯”可編程化,適合復(fù)雜/延遲/池化創(chuàng)建。
- 獲取“工廠本身”用:
&clientFactoryBean。 - 便于封裝復(fù)雜 SDK 實例化、動態(tài)代理實例、框架級 Bean。
何時選:構(gòu)造過程復(fù)雜、需要“拿結(jié)果不是拿工廠”的場景。
4) @Import 家族:裝配拼裝器
怎么用
- 直接導(dǎo)入配置類:
@Import(AppConfig.class) - 選擇器:
ImportSelector(按條件返回類名集合) - 低層注冊:
ImportBeanDefinitionRegistrar(手動注冊BeanDefinition)
特點
- 適合模塊化配置/Starter:把一組 Bean 一鍵裝配。
- 與
@Conditional組合,實現(xiàn)“自動裝配”的開/關(guān)。
何時選:框架/組件開發(fā)、Starter、按類路徑/環(huán)境動態(tài)裝配。
5) XML(<bean/>)
特點
- 歷史包袱/遺留系統(tǒng)常見;與 JavaConfig 可并存。
- 在強合規(guī)/模板化平臺仍有價值。
何時選:遺留項目、平臺強約束、需運行時熱替換 XML 的場景。
6) 運行時注冊:BeanDefinitionRegistry / GenericApplicationContext#registerBean
怎么用
context.registerBean("userRepo", UserRepo.class, () -> new UserRepo(ds));
特點
- 最靈活:可在運行中按條件裝配/卸載。
- 常用于框架擴展、動態(tài)多租戶/插件化。
何時選:框架層、插件/腳手架、動態(tài)場景。
二、怎么選?——一張可落地的決策清單
- 普通業(yè)務(wù)類 →
@Component(或語義化的@Service/@Repository),配合構(gòu)造器注入。 - 第三方對象/需要精細控制(連接池、客戶端、線程池)→
@Configuration + @Bean。 - 創(chuàng)建邏輯復(fù)雜/需要返回“產(chǎn)品而非工廠” →
FactoryBean。 - 模塊化/Starter/按條件成批裝配 →
@Import(ImportSelector/Registrar)+@Conditional。 - 遺留或平臺要求 → XML。
- 動態(tài)注冊/插件化 → 運行時注冊 API。
高頻口訣:“業(yè)組件、庫配 Bean、難用工廠、批量用 Import、老活交 XML、動態(tài)上 Registry。”
三、組合拳:把方式與“條件/環(huán)境/范圍/生命周期”拼起來
條件裝配:@Conditional / @Profile
- 例:僅在
prod激活某個@Bean;當(dāng)類路徑存在某依賴再裝配。
作用域:@Scope("singleton"|"prototype"|"request"|"session")
- 單例配
@Bean/@Component;原型慎用(生命周期管理在你)。
懶加載:@Lazy
- 對重量級 Bean 延遲創(chuàng)建,縮短冷啟動。
優(yōu)先/歧義:@Primary / @Qualifier("xxx")
- 多實現(xiàn)注入時避免“多候選”異常。
生命周期:initMethod/destroyMethod、@PostConstruct/@PreDestroy
- 池/連接類務(wù)必正確清理。
配置類性能:@Configuration(proxyBeanMethods = false)
- 若
@Bean之間不相互調(diào)用,設(shè) false 提升啟動性能。
四、常見踩坑與規(guī)避
- 同名/同類型沖突
- 現(xiàn)象:
NoUniqueBeanDefinitionException - 解法:約定 Bean 命名;
@Qualifier精確注入;需要默認實現(xiàn)時加@Primary。
- 原型 Bean 生命周期失控
- 容器只負責(zé)創(chuàng)建,不托管銷毀;注入到單例里極易內(nèi)存/狀態(tài)泄漏。
- 方案:
ObjectProvider/Provider延遲獲取,或改成無狀態(tài)。
@Configuration誤用導(dǎo)致“重復(fù)實例化”
- 在 Full 模式下通過代理保證同一
@Bean單例復(fù)用; - 設(shè)
proxyBeanMethods=false時跨方法互調(diào)會各自新建,需謹慎。
FactoryBean取到的是“產(chǎn)品不是工廠”
- 想拿工廠本身要用
&beanName。這點面試???。
- 條件/環(huán)境不生效
- 激活 profile 寫錯;
@ConditionalOnClass等判斷失敗。 - 啟動參數(shù)/環(huán)境變量要對齊:
spring.profiles.active=prod。
- AOP/事務(wù)不起效
- Bean 未交給容器管理、或在
@Bean方法里手動new(繞過代理)。 - 規(guī)則:所有需要切面的對象都由容器生產(chǎn)。
五、最佳實踐清單(可直接落地)
- 優(yōu)先用構(gòu)造器注入,配合
final字段保證不可變與可測試性。 - 業(yè)務(wù)類用
@Component家族,第三方對象用@Bean,職責(zé)清晰。 - Starter/框架層用
@Import + Conditional,形成模塊化裝配。 - 對重量 Bean 加
@Lazy,對多實現(xiàn)用@Qualifier,給默認實現(xiàn)標(biāo)@Primary。 - 配置類若無跨 @Bean 互調(diào),開啟
@Configuration(proxyBeanMethods = false)提升啟動性能。 - 需要復(fù)雜構(gòu)造/代理產(chǎn)物,優(yōu)先考慮
FactoryBean封裝。 - 原型 Bean 慎用,確需動態(tài)實例用
ObjectProvider拉取。 - 生命周期要閉環(huán):連接/線程池顯式
destroyMethod,或?qū)崿F(xiàn)DisposableBean。
寫在最后
把“誰創(chuàng)建、何時創(chuàng)建、在哪創(chuàng)建、如何銷毀”說清楚,就是 Bean 策略的全部。 業(yè)務(wù)常態(tài)化用 @Component;第三方與精細控制用 @Bean;復(fù)雜構(gòu)造上 FactoryBean;模塊化上 @Import;其余按場景增量選擇。 選對方式,系統(tǒng)啟動快、結(jié)構(gòu)清晰、擴展成本更低。
以上就是Spring創(chuàng)建Bean的多種方式對比與最佳實踐的詳細內(nèi)容,更多關(guān)于Spring創(chuàng)建Bean方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot應(yīng)用中靜態(tài)資源訪問與接口請求沖突問題解決
這篇文章主要介紹了springboot應(yīng)用中靜態(tài)資源訪問與接口請求沖突,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06
SpringBoot+Redis海量重復(fù)提交問題解決
在實際的開發(fā)項目中,一個對外暴露的接口往往會面臨很多次請求,所以本文介紹一下SpringBoot+Redis海量重復(fù)提交問題解決,感興趣的可以了解一下2023-12-12
Springboot PostMapping無法獲取數(shù)據(jù)問題及解決
這篇文章主要介紹了Springboot PostMapping無法獲取數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
詳解SpringBoot 快速整合MyBatis(去XML化)
本篇文章主要介紹了詳解SpringBoot 快速整合MyBatis(去XML化),非常具有實用價值,需要的朋友可以參考下2017-10-10
springboot?整合表達式計算引擎?Aviator?使用示例詳解
本文詳細介紹了Google?Aviator?這款高性能、輕量級的?Java?表達式求值引擎,并通過詳細的代碼操作演示了相關(guān)API的使用以及如何在springboot項目中進行集成,感興趣的朋友一起看看吧2024-08-08
詳解Maven profile配置管理及激活profile的幾種方式
這篇文章主要介紹了詳解Maven profile配置管理及激活profile的幾種方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

