歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之分析各 scope 的 bean 建立

精品專欄

 

在 Spring 中存在著不同的 scope,預設是 singleton ,還有 prototype、request 等等其他的 scope,他們的初始化步驟是怎樣的呢?這個答案在這篇部落格中給出。

singleton

Spring 的 scope 預設為 singleton,其初始化的程式碼如下:

  1.            if (mbd.isSingleton()) {

  2.                    sharedInstance = getSingleton(beanName, () -> {

  3.                        try {

  4.                            return createBean(beanName, mbd, args);

  5.                        }

  6.                        catch (BeansException ex) {

  7.                            destroySingleton(beanName);

  8.                            throw ex;

  9.                        }

  10.                    });

  11.                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

  12.                }

第一部分分析了從快取中獲取單例樣式的 bean,但是如果快取中不存在呢?則需要從頭開始載入 bean,這個過程由 getSingleton() 實現。

  1.  public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {

  2.        Assert.notNull(beanName, "Bean name must not be null");

  3.        // 全域性加鎖

  4.        synchronized (this.singletonObjects) {

  5.            // 從快取中檢查一遍

  6.            // 因為 singleton 樣式其實就是復用已經建立的 bean 所以這步驟必須檢查

  7.            Object singletonObject = this.singletonObjects.get(beanName);

  8.            //  為空,開始載入過程

  9.            if (singletonObject == null) {

  10.                // 省略 部分程式碼

  11.                // 載入前置處理

  12.                beforeSingletonCreation(beanName);

  13.                boolean newSingleton = false;

  14.                // 省略程式碼

  15.                try {

  16.                    // 初始化 bean

  17.                    // 這個過程其實是呼叫 createBean() 方法

  18.                    singletonObject = singletonFactory.getObject();

  19.                    newSingleton = true;

  20.                }

  21.                // 省略 catch 部分

  22.                }

  23.                finally {

  24.                    // 後置處理

  25.                    afterSingletonCreation(beanName);

  26.                }

  27.                // 加入快取中

  28.                if (newSingleton) {

  29.                    addSingleton(beanName, singletonObject);

  30.                }

  31.            }

  32.            // 直接傳回

  33.            return singletonObject;

  34.        }

  35.    }

其實這個過程並沒有真正建立 bean,僅僅只是做了一部分準備和預處理步驟,真正獲取單例 bean 的方法其實是由 singletonFactory.getObject() 這部分實現,而 singletonFactory 由回呼方法產生。那麼這個方法做了哪些準備呢?

  1. 再次檢查快取是否已經載入過,如果已經載入了則直接傳回,否則開始載入過程。

  2. 呼叫 beforeSingletonCreation() 記錄載入單例 bean 之前的載入狀態,即前置處理。

  3. 呼叫引數傳遞的 ObjectFactory 的 getObject() 實體化 bean。

  4. 呼叫 afterSingletonCreation() 進行載入單例後的後置處理。

  5. 將結果記錄並加入值快取中,同時刪除載入 bean 過程中所記錄的一些輔助狀態。

流程中涉及的三個方法 beforeSingletonCreation() 與 afterSingletonCreation() 在部落格 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 中分析過了,所以這裡不再闡述了,我們看另外一個方法 addSingleton()

  1.    protected void addSingleton(String beanName, Object singletonObject) {

  2.        synchronized (this.singletonObjects) {

  3.            this.singletonObjects.put(beanName, singletonObject);

  4.            this.singletonFactories.remove(beanName);

  5.            this.earlySingletonObjects.remove(beanName);

  6.            this.registeredSingletons.add(beanName);

  7.        }

  8.    }

一個 put、一個 add、兩個 remove。singletonObjects 單例 bean 的快取,singletonFactories 單例 bean Factory 的快取,earlySingletonObjects “早期”建立的單例 bean 的快取,registeredSingletons 已經註冊的單例快取。

載入了單例 bean 後,呼叫 getObjectForBeanInstance() 從 bean 實體中獲取物件。該方法已經在 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 詳細分析了。

原型樣式

  1.                else if (mbd.isPrototype()) {

  2.                    Object prototypeInstance = null;

  3.                    try {

  4.                        beforePrototypeCreation(beanName);

  5.                        prototypeInstance = createBean(beanName, mbd, args);

  6.                    }

  7.                    finally {

  8.                        afterPrototypeCreation(beanName);

  9.                    }

  10.                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);

  11.                }

原型樣式的初始化過程很簡單:直接建立一個新的實體就可以了。過程如下:

  1. 呼叫 beforeSingletonCreation() 記錄載入原型樣式 bean 之前的載入狀態,即前置處理。

  2. 呼叫 createBean() 建立一個 bean 實體物件。

  3. 呼叫 afterSingletonCreation() 進行載入原型樣式 bean 後的後置處理。

  4. 呼叫 getObjectForBeanInstance() 從 bean 實體中獲取物件。

其他作用域

  1. String scopeName = mbd.getScope();

  2.                    final Scope scope = this.scopes.get(scopeName);

  3.                    if (scope == null) {

  4.                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");

  5.                    }

  6.                    try {

  7.                        Object scopedInstance = scope.get(beanName, () -> {

  8.                            beforePrototypeCreation(beanName);

  9.                            try {

  10.                                return createBean(beanName, mbd, args);

  11.                            }

  12.                            finally {

  13.                                afterPrototypeCreation(beanName);

  14.                            }

  15.                        });

  16.                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

  17.                    }

  18.                    catch (IllegalStateException ex) {

  19.                        throw new BeanCreationException(beanName,

  20.                                "Scope '" + scopeName + "' is not active for the current thread; consider " +

  21.                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",

  22.                                ex);

  23.                    }

核心流程和原型樣式一樣,只不過獲取 bean 實體是由 scope.get() 實現,如下:

  1.    public Object get(String name, ObjectFactory> objectFactory) {

  2.       // 獲取 scope 快取

  3.        Map<String, Object> scope = this.threadScope.get();

  4.        Object scopedObject = scope.get(name);

  5.        if (scopedObject == null) {

  6.            scopedObject = objectFactory.getObject();

  7.            // 加入快取

  8.            scope.put(name, scopedObject);

  9.        }

  10.        return scopedObject;

  11.    }

對於上面三個模組,其中最重要的有兩個方法,一個是 createBean()、一個是 getObjectForBeanInstance()。這兩個方法在上面三個模組都有呼叫, createBean() 後續詳細說明, getObjectForBeanInstance() 在部落格 【死磕 Spring】----- 載入 bean 之 快取中獲取單例 bean 中有詳細講解,這裡再次闡述下(此段內容來自《Spring 原始碼深度解析》):這個方法主要是驗證以下我們得到的 bean 的正確性,其實就是檢測當前 bean 是否是 FactoryBean 型別的 bean,如果是,那麼需要呼叫該 bean 對應的 FactoryBean 實體的 getObject() 作為傳回值。無論是從快取中獲得到的 bean 還是透過不同的 scope 策略載入的 bean 都只是最原始的 bean 狀態,並不一定就是我們最終想要的 bean。舉個例子,加入我們需要對工廠 bean 進行處理,那麼這裡得到的其實是工廠 bean 的初始狀態,但是我們真正需要的是工廠 bean 中定義 factory-method 方法中傳回的 bean,而 getObjectForBeanInstance() 就是完成這個工作的。

至此,Spring 載入 bean 的三個部分(LZ自己劃分的)已經分析完畢了。

【死磕 Spring】----- IOC 之開啟 bean 的載入

【死磕 Spring】----- IOC 之從單例快取中獲取單例 bean

【死磕 Spring】----- IOC 之parentBeanFactory 與依賴處理

【死磕 Spring】----- IOC 之 IOC 初始化總結

END


>>>>>> 加群交流技術 <<<<<<

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖