手寫模擬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