答讀者問:關於隱式 id 重複的問題
來源:江南一點雨
我自己天天跟大夥講 Spring 原始碼,我基本都是分析原始碼來講。小夥伴們學習了之後,經常會產生許多千奇百怪的想法,這些想法都很不錯,往往這些想法還給了我很大的啟發,讓我發現原來這個問題還可以從這個角度來理解。
今天我們來看一個小夥伴的提問:
首先我得先誇一句,這個問題真的非常好!問題非常詳細,有原始碼有截圖有版本號,該說的都說了,問題非常清晰,我一看就知道發生了什麼事情,每天在微信上問松哥問題的人不少,能把問題說的這麼清楚的人屈指可數。
我跟大家講一下這個問題的上下文:
Spring 中 beanName 是不能重複的,一般情況下,我們在定義 Bean 的時候,都要為其指定 beanName 屬性,如果不指定,則會預設生成 beanName。在 XML 配置中,如果我們不指定 beanName 或者 id,那麼預設生成的 beanName 就是類名的完整路徑或者是 類名完整路徑+#+編號
。這個小夥伴就是在學習了上述內容之後,提出來這個問題。
關於 beanName 自動生成邏輯松哥在影片中都已經詳細介紹過了,因此這裡就簡單和大家梳理一下思路,具體可以參考 Spring 原始碼影片。
問題分析
小夥伴一共提出兩個問題,我們分別來看。
問題一
首先定義了一個 User 物件,但是並未指定 beanName,按照松哥之前在 Spring 原始碼影片中所講的,此時會自動給這個 bean 生成 id 和別名,別名是類名的完整路徑,即 cn.junhaox.entity.User
,id 則是類名完整路徑+編號,即 cn.junhaox.entity.User#0
,即我們可以透過這兩個任意一個名稱來訪問到第一個物件。
第二個 bean 在定義的時候,則指定了 id,而且指定的 id 恰好就是第一個 bean 自動生成的 id。
這個邏輯上顯然是衝突了,導致最終訪問的時候,透過 cn.junhaox.entity.User#0
或者 cn.junhaox.entity.User
訪問到的是第二個 bean 而不是第一個 bean,這就給人一種第一個 bean 似乎註冊失敗了的感覺。
我們先來分析一下這個問題。
先來說 bean 的註冊,當 bean 在註冊的時候,首先會去檢查當前 beanName 是否重複(具體在 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition) 方法中),但是這個檢查主要是檢查我們自己手動配置的 beanName 是否存在重複的情況,並不會去檢查自動生成的 beanName 是否重複,這就導致了當第二個 bean 在註冊的時候,檢查 beanName 是否重複的時候,結果發現 beanName 並不重複,因此就導致了 cn.junhaox.entity.User#0
beanName 重新指向了第二個 bean,那毫無疑問,cn.junhaox.entity.User
作為別名,也重新指向了第二個 bean。
這就是第一個問題產生的原因。
問題二
根據前面的分析,小夥伴們已經知道,對於第一個 bean,由於即沒有配置 id,又沒有配置 beanName,所以第一個 bean 在註冊的時候,會自動生成 id cn.junhaox.entity.User#0
並且會自動生成 beanName cn.junhaox.entity.User
。
現在第二個問題就是把第一個 bean 的別名作為第二個 bean 的 id 了,導致第二個 bean 似乎訪問不到了。
松哥先來說結論,這個問題其實目前在最新版的 Spring 中已經不存在了,具體的處理是在 2022 年 2 月 5 號提交的程式碼中解決了問題,在當年 3 月份釋出的 v6.0.0-M3 版本中這塊的程式碼改過來了,我們來看下程式碼的變化大家就明白了:
大家可以看到,變化發生在 DefaultListableBeanFactory#registerBeanDefinition 方法中,綠色的程式碼塊就是新增的程式碼。
新增的程式碼主要是當我們向容器註冊一個 BeanDefinition 的時候,首先會去檢查這個 beanName 是否是一個別名,如果是,則檢查別名是否允許覆蓋,如果別名不允許覆蓋,那麼該拋異常就拋異常,如果別名允許覆蓋,則呼叫 removeAlias 方法移除別名,這個移除相當於剪掉了別名之間的關係,cn.junhaox.entity.User
將不再作為別名指向 cn.junhaox.entity.User#0
了。
因此,對於第二個問題,從 Spring6.0.0-M3 開始,透過 cn.junhaox.entity.User#0
可以訪問到第一個 bean,透過 cn.junhaox.entity.User
則可以訪問到第二個 bean。
但是,在此版本之前,並未檢查當前 beanName 是否是一個別名,而是直接使用該 beanName 進行註冊。當我們去查詢 bean 的時候,都是根據 beanName 去查詢 bean 的,如果是根據型別,最終也會先根據型別找出 beanName,然後再去查詢 bean。根據 beanName 去搜尋 bean 的時候,會先根據別名鏈條確定出最終的 beanName,由於 cn.junhaox.entity.User
和 cn.junhaox.entity.User#0
之間還存在別名關係,因此當我們按照 beanName cn.junhaox.entity.User
去搜尋 bean 的時候,系統會找到這是 cn.junhaox.entity.User#0
的別名,進而找出來 cn.junhaox.entity.User#0
所對應的 bean 並返回,這就導致第二個 bean 將來無法被查詢到。
好啦,現在這兩個問題都搞明白了吧~
以上內容松哥主要是和大家分享思路,技術細節包括涉及到的 Spring 原始碼細節在之前的 Spring 影片中都講過,大家可以參考影片。
歡迎大家繼續提問!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2995591/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 快速解決mongodb出現id重複問題MongoDB
- 基於CNN的閱讀理解式問答模型:DGCNNCNN模型GC
- element UI元件樣式重複問題UI元件
- 關於Xilinx PCIE DMA的問答
- 關於 PHP Session ID 改變的問題解決PHPSession
- 解決 HttpServletRequest 的輸入流不能重複讀的問題HTTPServlet
- 答讀者問:BeanFactoryPostProcessor 似乎失效了?Bean
- 關於webpack問答記錄...Web
- 關於 pytest Case 遇到重試的問題
- RCmongodb出現id重複問題的簡單解決辦法jztMongoDB
- 面試中關於nginx的問答面試Nginx
- 關於cuda中的函式問題函式
- 髒讀!幻讀!不可重複讀!mysql併發事務引發的問題MySql
- 關於js執行緒問題的解讀JS執行緒
- 分散式重複提交問題架構設計思路分散式架構
- 關於取每個使用者最新一條留言去重的問題
- 資料檢視的重複問題
- 有關 Android Studio 重複引入包的問題和解決方案Android
- 基於BERT進行抽取式問答
- mysql隱式轉換問題MySql
- [20190918]關於函式索引問題.txt函式索引
- 關於this指向的問題
- 自問自答系列——關於 Laravel6.0 開發中的簡單小問題解答Laravel
- 關於onethink 目錄,檔案讀寫檢測函式中的問題函式
- 關於CSDN廣告打擾閱讀的問題
- Android studio glide包重複問題AndroidIDE
- 併發請求的重複插入問題
- 冴羽答讀者問:人生的意義是什麼?
- 關於 Angular view Query 的 id 選擇器問題的單步除錯AngularView除錯
- 關於移動路由器的一問一答路由器
- [20220811]奇怪的隱式轉換問題.txt
- 關於 Puerts 的效能問題
- 關於DrawerLayout的小問題
- 關於javascript的this指向問題JavaScript
- webpack dll打包重複問題優化Web優化
- 冴羽答讀者問:怎麼平衡工作與生活?
- 冴羽答讀者問:悄悄過來蹭個回答
- 冴羽答讀者問:怎麼才能不焦慮?