Spring底層原理探究

2020-10-12 11:00:09

Spring底層原理

前言

  離上一篇文章記錄差不多過了半年左右的時間吧,這段時間很多面試準備,知識點的學習基本被我轉移到了書面筆記上,秋招仍在繼續,你我怎能懈怠!特撿起上半年的老本,繼續自己的CSDN部落格之途。

  看過我之前寫的文章的小夥伴肯定都有一個感受,啊寫的好基礎呀!沒錯是的,之前無論是技術知識的積累還是知識層面的廣度和深度,都近似一個小白在寫雜談…

  這段很長時間的離開希望能讓以後我的分享提升一個檔次,至於為什麼要堅持寫部落格?

  1. 網際網路時代軟體行業技術開源精神的普及,分享技術是一件有助於自己,更幫助其他人從而達到共贏的很棒的事情。
  2. 幫助自己鞏固記憶知識點,好記性不如爛筆頭,同時費曼學習法教會我們如果能把別人講懂,說明我們已經熟練掌握了該知識點。
  3. 自己喜歡閱讀和寫作這兩件事,將自己想要表達的觀點和看法寫在文章裡,是一件很有成就感的事情。不管有沒有人閱讀,負面評論什麼的,自己從中可以獲取到樂趣。
目標

  由於仍處於秋招白熱化期間,我可能有時沉浸在自己面經的複習中,但我計劃至少兩天寫一篇原創文章。希望可以完成!

正文

  我們知道Spring是Jave EE裡面劃時代的輕量級框架設計,代替了之前重量級的EJB技術(Enterprise JavaBean),可以解決物件依賴問題,降低耦合性,同時提供宣告式事務管理功能。
  Spring兩個核心功能為 控制反轉(IoC)和提供面向切面程式設計(AOP)。

 今天我們就來了解下Spring的核心IoC容器,它是怎麼匯入bean並進行範例化物件操作的?

匯入Bean

第一個問題我們首先要知道我們通過哪幾種方式可以為IoC容器匯入bean?

  1. xml方式
    xml作為最基本的設定方式,其實是比較重量級比較繁瑣的一種方式,我們在xml檔案中宣告一個個的 <bean標籤,指定bean的id和要匯入的全類名。

  2. Java類設定
    使用Java類定義Spring設定。使用@Configuration註解需要作為設定的類。(表示該類將定義Bean)

    @Configuration
    public class ApplicationContextConfig{
    	@Bean
    	public String message() {
    		return "hello"}
    }
    
    
    
  3. 開發中我們用的最多的是元件註解,@Component, 常見的有@Controller, @Repository, @Service。

BeanDefinition

 上述幾種方式可以為我們的IoC容器匯入bean,那麼問題來了,我們匯入IoC容器中的bean 被 Spring 識別成什麼了呢,怎麼進行範例化的呢?

 我們知道我們可以通過容器提供的 getBean( )方法獲取到對應id的bean物件,但是在 getBean() 之前做了哪些操作呢?

 我們就要了解一下BeanDefinition這個物件了,我們被註解標記的類被JVM類載入器載入到記憶體中形成對應的BeanDedifinitionbean定義物件,儲存在一個map集合(BeanDefinitionMap)中。如下圖:
在這裡插入圖片描述


 相信很多朋友都沒聽說過Spring裡面的這個bean定義物件,沒關係,我們學習新知識都是從一開始什麼都不知道到了解概念再到熟練,精通。

 單單BeanDefinition可不能直接範例化,裡面需要涉及到一個介面,BeanFactoryPostProcessor,我們可以把它理解為在範例化物件之前可以修改我們的bean定義物件即BeanDedinition物件,這個介面中的一個實現類BeanDefinitionRegistryPostProcessor可以完成類似「設計小組」的功能,對位元組碼進行解析,這個介面的另外一個實現類可以實現類似「稽核小組」的功能,用於修改 bean定義物件。

 聽到這裡可能很多小夥伴已經雲裡霧裡了,「這什麼亂七八糟的,你的文筆就這,就這??」

 別急,我可以拿人給大家打個不恰當的比喻,一個已經範例化的bean物件可以看成是一個已經18歲的成年人了。

 這個BeanDefinition我們可以把它看作我們人最初的形態——受精卵,我們以後長成人的基因已經在這裡確定了,那上面所說的BeanFactoryPostProcessor可以對bean定義物件作修改的嘛,沒事啊,我們受精卵也可以作修改啊,不是有個「人類基因組計劃」嘛,基因工程可以改變bean定義物件啊。之後getBean() 獲取到範例物件,此時我們知道還有一個BeanPostProcessor介面(注意這個和上面的介面不一樣!),這個就相當於我們現在流行的整容變性啊,之後真正成長成我們想要的樣子了。(說多了都是淚啊!)

這就是匯入到IoC容器後bean被Spring識別的過程。

範例化Bean

 上面我們知道經過bean的定義解析過程,我們需要呼叫 getBean() 來範例化物件了,IoC容器是怎麼一步步範例化物件的呢?

 我們可以通過原始碼打斷點跟隨著getBean()的呼叫棧一步步地分析~
總體呼叫棧為如下所述:

getBean() ----> doGetBean() ----->getSingleton() ----->createBean() ------>doCreateBean(),到這裡我們停一下,這一步就是真正的建立我們的bean範例物件的過程。
 通過檢視原始碼我們知道它呼叫createBeanInstance()方法通過反射來建立物件(早期物件),注意此時物件只是一個空殼物件,並不能直接構成我們最後的單例物件(Spring的IoC容器預設是單例的)。這裡我們不得不簡單科普一下Spring中的三級快取了,不然咱們無法繼續下去。

Spring三級快取

一級快取:就是大名鼎鼎的單例快取池,用於儲存所有的單範例 bean。

	singletonObjects = new ConCurrentHashMap<>();	

終於看到ConCurrentHashMap的實際應用場景了hh。
三級快取:就是在createBeanInstance() 方法後建立的早期物件存放的地方。

	singletonFactories = new HashMap<>();	

我們總結:

  1. 所有的物件建立的過程中,都會去把自己的早期物件暴露到三級快取中去。
  2. 針對代理物件的時候,從三級快取到二級快取會經過一次代理,建立完畢後,就會刪除2級快取,把單例物件儲存到單例快取池中。

  緊接著上述建立早期物件,我們通過addSingletonFactory() 將早期物件暴露到三級快取中,再呼叫populateBean() 設定早期物件的屬性,如果發現其中依賴其他bean,則對其他bean執行同樣的步驟,如果出現迴圈依賴的問題(即A依賴B,B依賴A),Spring的三級快取可幫我們解決這個問題,我們在開發中並沒有注意到這個問題。

  以上就是今天總結分享的Spring匯入bean的三種方式以及匯入後bean的生命週期,由於博主也是沒有工作經驗的校招程式設計師,所以有很多地方說的不是很清楚甚至會引起誤解,大家可以提出來我一定會悉心聽取意見。

  如果喜歡我的文章可以點點贊關注我,我也計劃至少兩天一篇原創文章。如果你能看到這裡說明你肯定是一名熱愛學習的人,真誠希望我們能為了自己的目標或者夢想不斷努力,不斷學習,希望大家都能得到自己想要的! 我是 promise,一名喜歡思考人生,喜歡講大道理的程式設計師。