yii2 隨筆(七)依賴注入——(4)服務定位器
阿新 • • 發佈:2019-01-09
服務定位器定位器是依賴注入的一種解決方式,它包含依賴注入,在解決了依賴注入後,如果服務使用者和服務提供者不是用一個人,那麼使用者要了解提供服務的必須引數,這樣才能保證依賴的正確性,這就耦合了使用者和提供者,服務定位器就是解耦這部分的,服務提供者在 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使用的依賴注入和服務定位器,就說到這裡。