手寫模擬Spring底層原理-Bean的建立與獲取
作者:京東物流 張鼎元
1 引言
大家好,相信大家對Spring的底層原理都有一定的瞭解,這裡我們會針對Spring底層原理,在海量的Spring原始碼中進行抽絲剝繭手動實現一個Spring簡易版本,來促進我們對Spring架構有個更深的理解,對Spring的常用功能進行手寫模擬實現。
2 啟動Spring
針對Bean的建立和獲取功能,我們來進行功能的實
首先我們建立JdApplicationContext類做為Spring啟動類,實現bean的載入和獲取功能。
UserService和OrderService類作為Bean的實現類,通過JdApplicationContext類中的getBean方法獲取到前面兩個類的實現。
- App為啟動測試類
- AppConfig為啟動配置類
注:下面的程式碼會順著內容講解逐步完成
首先建立App類做為入口,測試Spring功能。通過初始化JdApplicationContext類,動態載入bean例項。 通過getBean方法獲取bean例項。
建立JdApplicationContext類,提供獲取Bean例項方法,通過建構函式動態初始化bean例項。
3 掃描類路徑並快取BeanDefinition資料
在JdApplicationContext類初始化的時候,通過AppConfig配置類獲取類的掃描路徑,在掃描路徑下,找到需要建立Bean的類,通過標註Component註解的類識別需要建立的Bean。
通過Component註解識別出的類,進行封裝成BeanDefinition. 再快取到beanDefinitionMap記憶體中。
上述的程式碼中,我們發現建立BeanDefinition類時,封裝了class類,beanName,scope三個主要屬性。用於建立bean的時候,提供class類進行初始化和屬性的注入,建立單例類或原型類提供資料依據。
4 初始化Bean和依賴注入
接下來,在上面的掃描操作完成後,所有待初始化的bean資料儲存beanDefinitionMap中。我們只需要遍歷beanDefinitionMap資料進行逐個初始化和屬性的注入。
上述程式碼中,對bean進行初始化時候,從beanDefinition中獲取要初始化的class,通過反射機構進行無參初始化。
初始化完成後,再對有Autowired註解的屬性進行依賴注入,Autowired註解沒有傳遞value值時預設取屬性名稱作為beanName,通過getBean方法獲取bean例項。
getBean方法會通過beanName,從beanDefinitionMap中取得beanDefinition資料。通過beanDefinition確認該bean為單例類原型類
如果為原型類,直接呼叫createBean方法進行bean初始化。
如果為單例類,首先從singletonBeanMap快取中獲取bean例項。如果未獲取到,呼叫createBean方法獲取bean例項,同時將已建立bean例項快取到singletonBeanMap快取中。
此時,在上述的功能中,依賴注入簡易版本已實現。同時我們注意到UserService和OrderService可能會產生迴圈依賴的問題,在這裡如何解決呢?
問題程式碼如下 :
上圖就是迴圈依賴問題程式碼導致的異常。重複建立bean進入死迴圈。
在初始化bean和屬性注入之間,我們可以增加二級快取作為突破口,解決死迴圈問題。
userService初始化後,需要注入orderService,通過getBean方法獲取,因為orderService沒有在singletonBeanMap快取中,也需要初始化並注入userService屬性, 同時userService還在初始化過程中,不能快取到singletonBeanMap快取中。造成彼此迴圈等待屬性的注入。為解決此問題,我們只需要設立初始化過程中快取到creatingBeanMap中,在userService初始化過後,未進行屬性注入前快取到creatingBeanMap中,userService需要的orderService屬性在建立bean例項過程中,優先從creatingBeanMap快取中得到userService例項,來完成bean例項的建立過程。orderService完成bean例項建立後,userService也相應的完成例項建立。
5 實現InitializingBean介面
在createBean過程中,我們可以對外提供初始化擴充套件介面InitializingBean介面。只要實現該介面,我們就可以針對bean的初始化進行擴充套件功能實現。 ![]
6 實現BeanPostProcessor介面模擬AOP
首先建立BeanPostProcessor介面,作為所有bean例項的對外擴充套件介面建立BeanPostProcessor介面實現類,模擬AOP功能,指定userService類進行切面。
在掃描類的時候,將已實現BeanPostProcessor介面類快取到beanPostProcessorList中。
通過上面的掃描,beanPostProcessorList已快取所有的BeanPostProcessor實現類。在createBean的時候,對已建立的bean例項進行預處理擴充套件。
通過上述程式碼的實現效果如下:
原始碼:
7 總結
在上述的講解中,我們對Spring底層原理進行簡單的實現,通過對類的掃描,註解標識的判斷,beanDefinition的定義和快取。通過反射和代理進行bean例項的建立和擴充套件。相信大家也看出來在實現過程中,有很多地方需要改進,還可以繼續擴充套件Spring很多其它功能。例如擴充套件beanDefinition的註冊,引入Bean工廠,延遲載入等。
- 應用健康度隱患刨析解決系列之資料庫時區設定
- 對於Vue3和Ts的心得和思考
- 一文詳解擴散模型:DDPM
- zookeeper的Leader選舉原始碼解析
- 一文帶你搞懂如何優化慢SQL
- 京東金融Android瘦身探索與實踐
- 微前端框架single-spa子應用載入解析
- cookie時效無限延長方案
- 聊聊前端效能指標那些事兒
- Spring竟然可以建立“重複”名稱的bean?—一次專案中存在多個bean名稱重複問題的排查
- 京東金融Android瘦身探索與實踐
- Spring原始碼核心剖析
- 深入淺出RPC服務 | 不同層的網路協議
- 安全測試之探索windows遊戲掃雷
- 關於資料庫分庫分表的一點想法
- 對於Vue3和Ts的心得和思考
- Bitmap、RoaringBitmap原理分析
- 京東小程式CI工具實踐
- 測試用例設計指南
- 當你對 redis 說你中意的女孩是 Mia