1. 程式人生 > >yii2 隨筆(七)依賴注入——(4)服務定位器

yii2 隨筆(七)依賴注入——(4)服務定位器

服務定位器定位器是依賴注入的一種解決方式,它包含依賴注入,在解決了依賴注入後,如果服務使用者和服務提供者不是用一個人,那麼使用者要了解提供服務的必須引數,這樣才能保證依賴的正確性,這就耦合了使用者和提供者,服務定位器就是解耦這部分的,服務提供者在 ServiceLocator 中註冊服務(同時註冊了依賴),僅僅告訴服務使用者那些服務的名稱或者別名,那麼對於服務提供者和使用者都是好的,使用者只需要知道提供者提供的什麼服務,而不必知道依賴什麼,服務提供者也不必為使用者“胡亂使用”服務而導致的bug所困擾。
那麼yii2是怎麼使用ServiceLocator呢?其實很簡單如下

//魔術方法,
public function __get($name){
    //得到某個註冊的方法
    if ($this->has($name)) {
        return $this->get($name);
    } else {
        return parent::__get($name);
    }
}
//魔術方法檢視某個服務是否存在,原始碼略
public function __isset($name){}
//__isset()中呼叫,檢視某個服務是否存在,原始碼略
public function has($id, $checkInstance = false){}
//得到某個服務
public function get($id, $throwException = true)
{
    if (isset($this->_components[$id])) {//如果是已經處理的服務,就直接返回
        return $this->_components[$id];
    }

    if (isset($this->_definitions[$id])) {//如定義了該服務
        $definition = $this->_definitions[$id];//得到服務的定義
        //如果服務是一個閉包,則把閉包註冊到已經例項化的服務中,並且返回閉包
        if (is_object($definition) && !$definition instanceof Closure) {
            return $this->_components[$id] = $definition;
        } else {//其他的情況下通過依賴注入生成物件,並且註冊為已處理,返回物件
            return $this->_components[$id] = Yii::createObject($definition);
        }
    } elseif ($throwException) {//如果丟擲異常,則丟擲異常
        throw new InvalidConfigException("Unknown component ID: $id");
    } else {//其他返回null
        return null;
    }
}
//註冊一個服務
public function set($id, $definition)
{
    if ($definition === null) {//如果該服務的定義為null,則刪除已經例項化的服務,返回空,用於登出已經例項化的並且儲存過的服務的定義
        unset($this->_components[$id], $this->_definitions[$id]);
        return;
    }
    //清空已經例項化的服務
    unset($this->_components[$id]);
    //如果該服務的定義為一個物件,並且是一個可呼叫的結構
    if (is_object($definition) || is_callable($definition, true)) {
        // an object, a class name, or a PHP callable
        $this->_definitions[$id] = $definition;
    } elseif (is_array($definition)) {//如果該服務是一個配置陣列
        // a configuration array
        if (isset($definition['class'])) {//如果有class鍵值,則直接註冊為一個服務的定義
            $this->_definitions[$id] = $definition;
        } else {//是配置陣列,但是沒有指定class,則丟擲異常
            throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element.");
        }
    } else {//什麼都不是,丟擲異常,非法註冊服務
        throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
    }
}
//清空已經例項化過的服務和定義,程式碼略
public function clear($id){}
//得到已經例項化後的服務,或者得到可用的服務配置
public function getComponents($returnDefinitions = true){}
//註冊所有的服務,這裡的$components,就是你在config裡寫的 $config['components']值
public function setComponents($components){}

那麼ServiceLocator是從什麼時候介入的呢?我們繼續開我們的index.php,注意下面那句話
(new yii\web\Application($config))->run();

我們檢視Application
class Application extends \yii\base\Application
//繼續追蹤  \yii\base\Application
abstract class Application extends Module
//繼續追蹤  Module
class Module extends ServiceLocator

哈,終於找到丫了!!!我們的application 其實就一個服務定位器,我們在配置檔案裡配置的components,都是application的這個服務定位器註冊的服務。這下知道為什麼叫做 setComponents這個函數了吧,不明白繼續往下看。

yii 用 set[typename] 的函式來確保屬性的可寫性,在基類 yii\base\Object 的建構函式裡使用了 Yii::configure($this, $config);這個會呼叫 setComponents 函式註冊服務。

好啦,前後都聯絡上了,yii2使用的依賴注入和服務定位器,就說到這裡。