springboot中@bean注解的創建和使用

bean的創建順序

在Spring Boot中,當一個配置類(使用@Configuration注解的類)中定義了多個bean時,這些bean的創建順序并不完全由它們在類中的聲明順序決定。Spring框架在創建和管理bean時,遵循了復雜的依賴注入和生命周期管理規則,這些規則決定了bean的創建和初始化順序。

以下是文心一言給出的一些影響bean創建順序的主要因素(我直接復制過來):

  • 依賴關系:Spring容器會根據bean之間的依賴關系來決定創建順序。如果一個bean依賴于另一個bean,那么被依賴的bean會首先被創建。Spring通過構造函數、setter方法或字段注入等方式來識別這些依賴關系。
  • @DependsOn注解:你可以使用@DependsOn注解來顯式指定一個bean依賴于其他一個或多個bean。被@DependsOn注解指定的bean會在當前bean之前被創建。
  • @Order或實現Ordered接口:雖然這些主要用于排序多個相同類型的bean(例如,多個實現了同一接口的bean),但它們在某些情況下也可能間接影響bean的創建順序,尤其是當這些bean之間存在依賴關系時。
  • @Bean的注冊順序:在配置類中,雖然bean的聲明順序不是決定性因素,但在沒有其他依賴關系或顯式排序的情況下,* Spring可能會按照它們在配置類中聲明的順序來創建bean。但是,這種順序并不是嚴格保證的,特別是當存在復雜的依賴關系時。
  • 初始化回調:Spring提供了幾種初始化回調方法(如@PostConstruct注解的方法或實現了InitializingBean接口的afterPropertiesSet方法),這些方法在bean的所有必要屬性被容器設置之后被調用。這些回調的執行順序也受bean之間的依賴關系影響。
  • 懶加載(Lazy Initialization):如果bean被標記為懶加載(通過@Lazy注解或全局配置),那么它只會在首次被請求時創建,這可能會影響bean的創建順序。
  • 總結來說,Spring Boot中配置類中多個bean的創建順序主要由bean之間的依賴關系決定,而不僅僅是它們在配置類中的聲明順序。因此,在設計應用時,應該盡量避免對bean創建順序的隱式依賴,而是通過顯式的依賴關系或配置來管理bean的創建和初始化順序。

bean注解的創建

首先這個注解在方法上使用,也可以在注解使用,這里只介紹在方法上使用的情況
在這里插入圖片描述
在方法上使用很簡單,只需要把它放在方法上就行
e.g

    @Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}
  • 這樣如果在類中當做類屬性使用,我們直接使用@Autowired注解注入就好了,

那如果是下面這樣呢,這個

    @Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

這樣根據基于springboot的自動裝配類型中的基于類型裝配,可以找到我們上面創建的那個DataSource類型的bean

spring的依賴注入

實際上在Spring框架中,當你使用@Bean注解來聲明一個bean的創建方法時,該方法中的參數并不是直接從某個地方“自動”獲取的,而是根據Spring的依賴注入(DI)機制來解決的。Spring容器在創建bean時,會分析@Bean方法中的參數,并嘗試通過以下幾種方式來解決這些參數的依賴:

  • 自動裝配(Autowiring):
  1. 基于類型(byType):Spring會嘗試在容器中查找與參數類型相匹配的bean。如果容器中只有一個bean匹配該類型,Spring會自動注入這個bean。如果有多個bean匹配,并且沒有使用@Qualifier注解來指定具體的bean名稱,那么Spring會拋出異常,因為它不知道應該注入哪一個bean。
  2. 基于名稱(byName):如果你的@Bean方法參數名與容器中某個bean的名稱相匹配,并且Spring的配置中啟用了基于名稱的自動裝配(這通常是默認行為),那么Spring會嘗試注入這個bean。不過,需要注意的是,在@Bean方法中使用基于名稱的自動裝配并不是非常直觀,因為@Bean方法的參數名在編譯后可能會被優化或更改,這取決于JVM和編譯器的設置。因此,更推薦使用基于類型的自動裝配。
  • 通過方法參數中的注解:
  1. 如果@Bean方法的參數上使用了如@Qualifier、@Value等注解,Spring會根據這些注解來解析參數的值。例如,@Qualifier注解可以用來指定應該注入哪個bean(在有多個候選bean的情況下)。@Value注解則通常用于注入配置文件中的值(如屬性文件中的值)。
  2. 通過構造函數或setter方法:
    需要注意的是,雖然這里討論的是@Bean方法中的參數,但通常我們不會在@Bean方法內部直接創建依賴對象(即參數所代表的bean)。相反,我們會讓Spring通過構造函數或setter方法將這些依賴注入到我們的bean中。然而,對于@Bean方法本身,其參數是通過上述的依賴注入機制來解決的。
  3. Java配置和@Configuration類:
    在@Configuration注解的類中,@Bean方法之間可以相互引用,因為Spring會確保在調用一個@Bean方法之前,它所依賴的所有bean都已經被創建和初始化。這種機制使得我們可以在@Bean方法中引用其他@Bean方法聲明的bean。
    總之,@Bean方法中的參數值是通過Spring的依賴注入機制來解決的,這通常涉及到基于類型或名稱的自動裝配,以及方法參數上的注解。

bean的名稱

  • 每個bean都有一個名稱,那使用@Bean注解產生的bean在容器中的bean的名稱什么,在下面有兩個DataSource類型的bean
@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/*** 這里bean沒有指定名稱那這個bean在容器中的名稱就 是方法名 "slaveDataSource"* @param druidProperties* @return*/@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}

在@bean注解中有個name參數,根據描述我們可以看出name值就是這個bean的名稱,其中If left unspecified, the name of the bean is the name of the annotated method,表示如果沒有指定,那這個bean的名稱就是@Bean注解所注釋的方法的名稱,所以上面兩個bean的名稱分別是masterDataSource 和 slaveDataSource
在這里插入圖片描述

  • 如果指定了默認名稱,那么這個bean在容器里就叫dynamicDataSource
    @Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);}

那么參數中 public DynamicDataSource dataSource(DataSource masterDataSource)中的形參masterDataSource來自哪里呢,實際上它是spring從容器中找一個類型為DataSource,名為 masterDataSource的bean,如果把這里改成下面這樣就會報錯,因為容器中現在有兩個DataSource類型的bean,masterDataSource 和 slaveDataSource,這里形參名叫做dataSource,spring根據名稱找不到,根據類型能找到兩個,不知道注入哪一個,就會報錯

@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource1(DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

這個時候@Qualifier注解就可以使用了,可以用@Qualifier注解指定bean,將masterDataSource的值 賦值到形參 dataSource

 /**** 這里的形參 dataSource指的容器中DataSource類型的 名為dataSource 的 bean* 但是這里面容器里面沒有這個bean,就可以用@Qualifier注解指定bean,將masterDataSource的值 賦值到形參 dataSource*/@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource1(@Qualifier("masterDataSource") DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

這里實際上還有個@Primary注解,假如有多個相同類型的bean,可以使用@Primary來標明優先用那個bean,但是同一種類型的bean,只能有一個使用@Primary注解,實際上DynamicDataSource是DataSource的子類,所以實際上它們是同一種bean,所以只能有一個@Primary注解

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/44032.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/44032.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/44032.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

qt connect 函數詳解

在 Qt 框架中&#xff0c;connect 函數是一個非常重要的機制&#xff0c;用于在信號&#xff08;signal&#xff09;和槽&#xff08;slot&#xff09;之間建立連接。信號和槽是 Qt 中用于對象間通信的一種機制。當某個特定事件發生時&#xff0c;一個對象可以發射&#xff08;…

短鏈接day3

短鏈接分組模塊 新增短鏈接分組 在新增之前&#xff0c;需要判斷gid是否是唯一的。 //檢查gid是否已存在&#xff0c;保證gid唯一public boolean hasGid(String gid){LambdaQueryWrapper<GroupDO> queryWrapper Wrappers.lambdaQuery(GroupDO.class).eq(GroupDO::getG…

在Windows中使用開源高性能編輯器Zed(持續更新)

簡介 “Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter. It’s also open source.” “Zed是一款高性能的支持多人協作的代碼編輯器&#xff0c;由Atom和Tree-sitter的創建者開發。它也是開源的。” Zed主打“高性能”&…

為什么選擇開放式耳機?悠律凝聲環開放式耳機體驗

開放式耳機相對于其他傳統耳機優勢有以下幾點&#xff1a; 配戴舒適性更好&#xff1a;由于開放式耳機的背面是開放的&#xff0c;空氣可以自由流動&#xff0c;減少了耳朵的悶熱感。長時間佩戴時更加舒適。&#xff0c;而傳統入耳式耳機一般都是塞入耳道&#xff0c;久戴會脹…

Java中的 this 關鍵字是什么意思? this() 又是什么?

目錄 問題問題一&#xff1a;什么是this關鍵字?問題二&#xff1a;什么是this()&#xff1f; 問題 問題一&#xff1a;什么是this關鍵字? 定義&#xff1a;this 代表當前對象。這個定義比較抽象&#xff0c;舉例來回答。 思考一個問題&#xff1a;如果沒有 this 會怎樣&…

鴻蒙開發學習筆記2

一、class 類 類是用于 創建對象模版。同時類聲明也會引入一個 新類型&#xff0c;可定義其 實例屬性、方法 和 構造函數。 // 類名 首字母大寫&#xff08;規范&#xff09; class 類名 {// 1、實例屬性&#xff08;字段&#xff09;// 2、構造函數// 3、方法 }1、屬性&…

IO練習網絡爬蟲獲取

題目&#xff1a; 具體文字內容如下&#xff1a; 練習&#xff1a;制造假數據 需求&#xff1a;制造假數據是開發中的一項重要能力&#xff0c;從各個網站爬取數據是其中的一種方法。 獲取姓氏示例及鏈接&#xff1a; 趙錢孫李 周吳鄭王 鏈接&#xff1a;百家姓_詩詞_百度漢…

如何追蹤ping連接中的所有路由器的數量和IP

如何快速判斷ping連接經過的路由器個數和IP&#xff1f; 方法一&#xff1a; ping命令會返回一個TTL&#xff0c;TTL&#xff08;Time To Live&#xff09;存活時間&#xff0c;一般初始值為64&#xff0c;每經過一個路由器就減一&#xff0c;當TTL為0時丟棄網絡包&#xff0…

“Numpy數據分析與挖掘:高效學習重點技能“

目錄 # 開篇 # 補充 zeros & ones eye 1. numpy數組的創建 1.1 array 1.2 range 1.3 arange 1.4 常見的數據類型 1.5 astype 1.6 random.random() & round 2. numpy數組計算和數組計算 2.1 reshape 2.2 shape 2.3 將一維數組變成多維數組 2.4 指定一維…

0010基于免疫遺傳算法的配送中心選址

免疫優化算法&#xff08;Immune Optimization Algorithm, IOA&#xff09;在物流配送中心選址中的應用是通過模擬免疫系統的進化過程來解決選址優化問題。物流配送中心選址問題涉及到如何在給定區域內選擇最優的位置&#xff0c;以最大化服務覆蓋并最小化運輸成本。 免疫優化…

我的FPGA

1.安裝quartus 2.更新usb blaster驅動 3.新建工程 1.隨便找一個文件夾&#xff0c;里面新建demo文件夾&#xff0c;表示一個個工程 在demo文件夾里面&#xff0c;新建src&#xff08;源碼&#xff09;&#xff0c;prj&#xff08;項目&#xff09;&#xff0c;doc&#xff…

HTTP代理的用途有哪些-okeyproxy

通過HTTP代理&#xff0c;客戶端可以間接訪問目標伺服器&#xff0c;從而實現多種功能。無論你是普通用戶還是技術大咖&#xff0c;HTTP代理都能帶來諸多便利和安全保障。本文將從多個角度詳細探討HTTP代理的用途。 HTTP代理的重要用途 1. 訪問控制和內容過濾 在企業和教育機…

什么是語音降噪?

當我們使用手機或者電腦進行語音通話時&#xff0c;有時候會聽到背景噪音干擾&#xff0c;比如人聲、電視聲或者風扇聲。這些噪音讓我們的通話變得不清晰&#xff0c;影響了溝通效果。那么&#xff0c;有沒有什么方法可以讓我們的語音通話更清晰呢&#xff1f;這就要介紹一下語…

壓縮感知2——算法模型

采集原理 其中Y就是壓縮后的信號表示(M維)&#xff0c;Φ表示采集的測量矩陣&#xff0c;可以是一個隨機矩陣&#xff0c;X代表原始的數字信號&#xff08;N維&#xff09;。 常見的測量矩陣——隨機高斯矩陣 隨機伯努利矩陣 稀疏隨機矩陣等&#xff0c;矩陣需要滿足與信號的稀…

spring xml實現bean對象(僅供自己參考)

對于spring xml來實現bean 具體代碼&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaL…

修改ES索引名稱

1 案例背景 將ES索引【my-index】修改為【my-index-v1】&#xff0c;方便添加索引別名 2 操作步驟 首先通過PUT請求將舊索引my-index設置為可寫&#xff08;如果之前設置為不可寫&#xff09; PUT /my-index/_settings {"settings": {"index.blocks.write&q…

AWS無服務器 應用程序開發—第十七章 Application Composer

Application Composer 是 AWS 提供的一種可視化工具,用于設計和構建無服務器應用程序。它通過拖放界面簡化了無服務器架構的創建過程,使開發者能夠更直觀地設計和配置應用程序的各個組件。 主要功能 可視化設計 通過拖放界面,開發者可以輕松地添加和配置 AWS 資源,如 L…

2-29 基于matlab的CEEMD

基于matlab的CEEMD&#xff08;Complementary Ensemble Empirical Mode Decomposition&#xff0c;互補集合經驗模態分解&#xff09;&#xff0c;先將數據精心ceemd分解&#xff0c;得到imf分量&#xff0c;然后通過相關系數帥選分量&#xff0c;在求出他們的樣本熵的特征。用…

昇思訓練營打卡第二十一天(DCGAN生成漫畫頭像)

DCGAN&#xff0c;即深度卷積生成對抗網絡&#xff08;Deep Convolutional Generative Adversarial Network&#xff09;&#xff0c;是一種深度學習模型&#xff0c;由Ian Goodfellow等人在2014年提出。DCGAN在生成對抗網絡&#xff08;GAN&#xff09;的基礎上&#xff0c;引…

【CentOS】Linux命令之docker命令(持續更新)

刪除所有容器 該命令將刪除所有已停止的容器。你還可以使用其他狀態值&#xff0c;例如created、restarting或dead docker container rm $(docker container ls -aqf statusexited)刪除所有鏡像 該命令將刪除所有鏡像&#xff0c;包括被使用的鏡像。請注意&#xff0c;如果某…