1. 程式人生 > >spring深入學習(十七) IOC 之分析各 scope 的 bean 建立

spring深入學習(十七) IOC 之分析各 scope 的 bean 建立

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

singleton

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

            if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

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

  public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");

        // 全域性加鎖
        synchronized (this.singletonObjects) {
            // 從快取中檢查一遍
            // 因為 singleton 模式其實就是複用已經建立的 bean 所以這步驟必須檢查
            Object singletonObject = this.singletonObjects.get(beanName);
            //  為空,開始載入過程
            if (singletonObject == null) {
                // 省略 部分程式碼

                // 載入前置處理
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                // 省略程式碼
                try {
                    // 初始化 bean
                    // 這個過程其實是呼叫 createBean() 方法
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                // 省略 catch 部分
                }
                finally {
                    // 後置處理
                    afterSingletonCreation(beanName);
                }
                // 加入快取中
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            // 直接返回
            return singletonObject;
        }
    }

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

  1. 再次檢查快取是否已經載入過,如果已經載入了則直接返回,否則開始載入過程。
  2. 呼叫 beforeSingletonCreation() 記錄載入單例 bean 之前的載入狀態,即前置處理。
  3. 呼叫引數傳遞的 ObjectFactory 的 getObject()
     例項化 bean。
  4. 呼叫 afterSingletonCreation() 進行載入單例後的後置處理。
  5. 將結果記錄並加入值快取中,同時刪除載入 bean 過程中所記錄的一些輔助狀態。

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

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

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

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

原型模式

                else if (mbd.isPrototype()) {
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

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

  1. 呼叫 beforeSingletonCreation() 記錄載入原型模式 bean 之前的載入狀態,即前置處理。
  2. 呼叫 createBean() 建立一個 bean 例項物件。
  3. 呼叫 afterSingletonCreation() 進行載入原型模式 bean 後的後置處理。
  4. 呼叫 getObjectForBeanInstance() 從 bean 例項中獲取物件。

其他作用域

String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }

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

    public Object get(String name, ObjectFactory<?> objectFactory) {
       // 獲取 scope 快取
        Map<String, Object> scope = this.threadScope.get();
        Object scopedObject = scope.get(name);
        if (scopedObject == null) {
            scopedObject = objectFactory.getObject();
            // 加入快取
            scope.put(name, scopedObject);
        }
        return scopedObject;
    }

對於上面三個模組,其中最重要的有兩個方法,一個是 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() 就是完成這個工作的。