<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      yii2之依賴注入與依賴注入容器

      一、為什么需要依賴注入

        首先我們先不管什么是依賴注入,先來分析一下沒有使用依賴注入會有什么樣的結果。假設我們有一個gmail郵件服務類GMail,然后有另一個類UserUser類需要使用發郵件的功能,于是我們在User類中定義一個成員變量$mailServer,并且在聲明這個變量的時候就給它賦值一個GMail類對象,或者在User構造函數中進行GMail類實例化與賦值。這樣寫程序會有什么問題呢?試想一下,每次當我們需要把User使用的郵件服務改為其他類型郵件服務的時候,我們需要頻繁修改User類的成員變量$mailServer,這樣是不好的。問題的根源就在于,我們不該把User類的成員變量$mailServer的實例化寫死在User類內部,而應該在調用User類的時候可以動態決定賦值給$mailServer的對象類型,依賴注入就是來解決這個問題的。

       

      二、依賴注入是什么

        所謂依賴注入,實質上就是當某個類對象需要使用另一個類實例的時候,不在類內部實例化另一個類,而將實例化的過程放在類外面實現,實例化完成后再賦值給類對象的某個屬性。這樣的話該類不需要知道賦值給它的屬性的對象具體屬于哪個類的,當需要改變這個屬性的類型的時候,無需對這個類的代碼進行任何改動,只需要在使用該類的地方修改實例化的代碼即可。

        依賴注入的方式有兩種:1.構造函數注入,將另一個類的對象作為參數傳遞給當前類的構造函數,在構造函數中給當前類屬性賦值;2.屬性注入,可以將該類某個屬性設置為public屬性,也可以編寫這個屬性的setter方法,這樣就可以在類外面給這個屬性賦值了。

       

      三、依賴注入容器

        仔細思考一下,我們會發現,雖然依賴注入解決了可能需要頻繁修改類內部代碼的問題,但是卻帶來了另一個問題。每次我們需要用到某個類對象的時候,我們都需要把這個類依賴的類都實例化,所以我們需要重復寫這些實例化的代碼,而且當依賴的類又依賴于其他類的時候,我們還要找出所有依賴類依賴的其他類然后實例化,可想而知,這是一個繁瑣低效而又麻煩且容易出錯的過程。這個時候依賴注入容器應運而生,它就是來解決這個問題的。

        依賴注入容器可以幫我們實例化和配置對象及其所有依賴對象,它會遞歸分析類的依賴關系并實例化所有依賴,而不需要我們去為這個事情費神。

        在yii2.0中,yii\di\Container就是依賴注入容器,這里先簡單說一下這個容器的使用。我們可以使用該類的set()方法來注冊一個類的依賴,把依賴信息傳遞給它就可以了,如果希望這個類是單例的,則可以使用setSingleton()方法注冊依賴。注冊依賴之后,當你需要這個類的對象的時候,使用Yii::createObject(),把類的配置參數傳遞過去,yii\di\Container即會幫你解決這個類的所有依賴并創建一個對象返回。

       

      四、yii依賴注入容器 - 依賴注冊

        好了,下面開始分析一下yii的依賴注入容器的實現原理。首先來看一下Container的幾個成員變量:

      /**
       * @var array 存儲單例對象,數組的鍵是對象所屬類的名稱
       */
      private $_singletons = [];
      
      /**
       * @var array 存儲依賴定義,數組的鍵是對象所屬類的名稱
       */
      private $_definitions = [];
      
      /**
       * @var array 存儲構造函數參數,數組的鍵是對象所屬類的名稱
       */
      private $_params = [];
      /**
       * @var array 存儲類的反射類對象,數組的鍵是類名或接口名
       */
      private $_reflections = [];
      
      /**
       * @var array 存儲類的依賴信息,數組的鍵是類名或接口名
       */
      private $_dependencies = [];

      其中前三個是用于依賴注冊的時候存儲一些類參數和依賴定義的,后兩個則是用于存儲依賴信息的,這樣使用同一個類的時候不用每次都進行依賴解析,直接使用這兩個變量緩存的依賴信息即可。

        接下來看看依賴注冊的兩個方法:

      /**
       * 在DI容器注冊依賴(注冊之后每次請求都將返回一個新的實例)
       * @param string $class:類名、接口名或別名
       * @param array $definition:類的依賴定義,可以是一個PHP回調函數,一個配置數組或者一個表示類名的字符串
       * @param array $params:構造函數的參數列表,在調用DI容器的get()方法獲取類實例的時候將被傳遞給類的構造函數
       * @return \yii\di\Container
       */
      public function set($class, $definition = [], array $params = [])
      {
      	$this->_definitions[$class] = $this->normalizeDefinition($class, $definition);//保存類配置信息
      	$this->_params[$class] = $params;//保存構造函數參數列表
      	unset($this->_singletons[$class]);//若存在單例依賴信息則刪除
      	return $this;
      }
      
      /**
       * 在DI容器注冊依賴(注冊之后每次請求都將返回同一個實例)
       * @param string $class:類名、接口名或別名
       * @param array $definition:類的依賴定義,可以是一個PHP回調函數,一個配置數組或者一個表示類名的字符串
       * @param array $params:構造函數的參數列表,在調用DI容器的get()方法獲取類實例的時候將被傳遞給類的構造函數
       * @return \yii\di\Container
       */
      public function setSingleton($class, $definition = [], array $params = [])
      {
      	$this->_definitions[$class] = $this->normalizeDefinition($class, $definition);
      	$this->_params[$class] = $params;
      	$this->_singletons[$class] = null;//賦值null表示尚未實例化
      	return $this;
      }

      這兩個方法很簡單,就是把依賴注冊傳入的參數信息保存下來,提供給實例化過程使用。這兩個方法中都調用了normalizeDefinition()方法,這個方法只是用于規范依賴定義的,源碼如下:

      /**
       * 規范依賴定義
       * @param string $class:類名稱
       * @param array $definition:依賴定義
       * @return type
       * @throws InvalidConfigException
       */
      protected function normalizeDefinition($class, $definition)
      {
      	if (empty($definition)) {//若為空,將$class作為類名稱
      		return ['class' => $class];
      	} elseif (is_string($definition)) {//若是字符串,默認其為類名稱
      		return ['class' => $definition];
      	} elseif (is_callable($definition, true) || is_object($definition)) {//若是PHP回調函數或對象,直接作為依賴的定義
      		return $definition;
      	} elseif (is_array($definition)) {//若是數組則需要確保包含了類名稱
      		if (!isset($definition['class'])) {
      			if (strpos($class, '\\') !== false) {
      				$definition['class'] = $class;
      			} else {
      				throw new InvalidConfigException("A class definition requires a \"class\" member.");
      			}
      		}
      		return $definition;
      	} else {
      		throw new InvalidConfigException("Unsupported definition type for \"$class\": " . gettype($definition));
      	}
      }
      

       

      五、yii依賴注入容器 - 對象實例化

        接下來就是重頭戲了,yii依賴注入容器是怎么根據依賴注冊的信息實現對象實例化的呢?我們一步一步來分析。在第三部分我們說到,當需要創建一個類對象的時候,我們調用的時候Yii::createObject()方法,這個方法里面調用的是Containerget()方法。為了使得講解的思路更清晰,這里我們先來看一下Container的另外兩個方法,getDependencies()resolveDependencies(),它們分別用于解析類的依賴信息和解決類依賴,會在對象實例化的過程中被調用。

        下面先來看看getDependencies()方法:

      /**
       * 解析指定類的依賴信息(利用PHP的反射機制)
       * @param string $class:類名、接口名或別名
       * @return type
       */
      protected function getDependencies($class)
      {
      	if (isset($this->_reflections[$class])) {//存在該類的依賴信息緩存,直接返回
      		return [$this->_reflections[$class], $this->_dependencies[$class]];
      	}
      
      	$dependencies = [];
      	$reflection = new ReflectionClass($class);//創建該類的反射類以獲取該類的信息
      
      	$constructor = $reflection->getConstructor();
      	if ($constructor !== null) {
      		foreach ($constructor->getParameters() as $param) {//遍歷構造函數參數列表
      			if ($param->isDefaultValueAvailable()) {//若存在默認值則直接使用默認值
      				$dependencies[] = $param->getDefaultValue();
      			} else {//獲取參數類型并創建引用
      				$c = $param->getClass();
      				$dependencies[] = Instance::of($c === null ? null : $c->getName());
      			}
      		}
      	}
      
      	//保存該類的反射類對象和依賴信息
      	$this->_reflections[$class] = $reflection;
      	$this->_dependencies[$class] = $dependencies;
      
      	return [$reflection, $dependencies];
      }

      首先判斷該類是已被解析過,如果是,直接返回緩存中該類的依賴信息,否則,利用PHP的反射機制對類的依賴進行分析,最后將分析所得依賴信息緩存,具體步驟已在代碼中注明。其中Instance類實例用于表示一個指定名稱的類的對象引用,也就是說getDependencies()方法調用之后,得到的$dependencies只是某個類的依賴信息,指明這個類依賴于哪些類,還沒有將這些依賴的類實例化,這個工作是由resolveDependencies()方法來完成的。

         再來看看resolveDependencies()方法:

      /**
       * 解決依賴
       * @param array $dependencies:依賴信息
       * @param ReflectionClass $reflection:放射類對象
       * @return type
       * @throws InvalidConfigException
       */
      protected function resolveDependencies($dependencies, $reflection = null)
      {
      	foreach ($dependencies as $index => $dependency) {//遍歷依賴信息數組,把所有的對象引用都替換為對應類的實例對象
      		if ($dependency instanceof Instance) {
      			if ($dependency->id !== null) {//組件id不為null,以id為類名實例化對象
      				$dependencies[$index] = $this->get($dependency->id);
      			} elseif ($reflection !== null) {//若id為null但$reflection不為null,通過$reflection獲取構造函數類型,報錯。。
      				$name = $reflection->getConstructor()->getParameters()[$index]->getName();
      				$class = $reflection->getName();
      				throw new InvalidConfigException("Missing required parameter \"$name\" when instantiating \"$class\".");
      			}
      		}
      	}
      	return $dependencies;
      }

      這個方法就是遍歷getDependencies()方法得到的關于某個類的依賴信息數組,對每個依賴的類調用Containerget()方法來獲取對象實例化。前面說到get()函數實例化過程中會調用這個方法,而這里又調用了get()方法,所以已經可以知道,依賴解析的過程其實是一個遞歸解析的過程。

        再回頭來看看get()方法:

      /**
       * DI容器返回一個請求類的實例
       * @param string $class:類名
       * @param array $params:構造函數參數值列表,按照構造函數中參數的順序排列
       * @param array $config:用于初始化對象屬性的配置數組
       * @return type
       * @throws InvalidConfigException
       */
      public function get($class, $params = [], $config = [])
      {
      	if (isset($this->_singletons[$class])) {//若存在此類的單例則直接返回單例
      		return $this->_singletons[$class];
      	} elseif (!isset($this->_definitions[$class])) {//若該類未注冊依賴,調用build()函數,使用PHP的反射機制獲取該類的依賴信息,解決依賴,創建類對象返回
      		return $this->build($class, $params, $config);
      	}
      
      	$definition = $this->_definitions[$class];
      
      	if (is_callable($definition, true)) {//若依賴定義是一個PHP函數則直接調用這個函數創建對象
      		$params = $this->resolveDependencies($this->mergeParams($class, $params));//解決依賴
      		$object = call_user_func($definition, $this, $params, $config);
      	} elseif (is_array($definition)) {//若依賴定義為數組,則合并參數,創建對象
      		$concrete = $definition['class'];
      		unset($definition['class']);
      
      		$config = array_merge($definition, $config);
      		$params = $this->mergeParams($class, $params);
      
      		if ($concrete === $class) {//遞歸解析終止
      			$object = $this->build($class, $params, $config);
      		} else {//遞歸進行依賴解析
      			$object = $this->get($concrete, $params, $config);
      		}
      	} elseif (is_object($definition)) {//若依賴定義為對象則保存為單例
      		return $this->_singletons[$class] = $definition;
      	} else {
      		throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition));
      	}
      
      	if (array_key_exists($class, $this->_singletons)) {//若該類注冊過單例依賴則實例化之
      		$this->_singletons[$class] = $object;
      	}
      
      	return $object;
      }

      首先判斷是否存在所需類的單例,若存在則直接返回單例,否則判斷該類是否已注冊依賴,若已注冊依賴,則根據注冊的依賴定義創建對象,具體每一步已在代碼注釋說明。其中mergeParams()方法只是用來合并用戶指定的參數和依賴注冊信息中的參數。若未注冊依賴,則調用build()方法,這個方法又干了些什么呢?看源碼:

      /**
       * 創建指定類的對象
       * @param string $class:類名稱
       * @param array $params:構造函數參數列表
       * @param array $config:初始化類對象屬性的配置數組
       * @return type
       * @throws NotInstantiableException
       */
      protected function build($class, $params, $config)
      {
      	list ($reflection, $dependencies) = $this->getDependencies($class);//獲取該類的依賴信息
      
      	foreach ($params as $index => $param) {//將構造函數參數列表加入該類的依賴信息中
      		$dependencies[$index] = $param;
      	}
      
      	$dependencies = $this->resolveDependencies($dependencies, $reflection);//解決依賴,實例化所有依賴的對象
      	if (!$reflection->isInstantiable()) {//類不可實例化
      		throw new NotInstantiableException($reflection->name);
      	}
      	if (empty($config)) {//配置數組為空,使用依賴信息數組創建對象
      		return $reflection->newInstanceArgs($dependencies);
      	}
      
      	if (!empty($dependencies) && $reflection->implementsInterface('yii\base\Configurable')) {
      		$dependencies[count($dependencies) - 1] = $config;//按照 Object 類的要求,構造函數的最后一個參數為 $config 數組
      		return $reflection->newInstanceArgs($dependencies);
      	} else {//先使用依賴信息創建對象,再使用$config配置初始化對象屬性
      		$object = $reflection->newInstanceArgs($dependencies);
      		foreach ($config as $name => $value) {
      			$object->$name = $value;
      		}
      		return $object;
      	}
      }

      首先,由于沒有類的依賴信息,調用getDependencies()方法分析得到依賴信息。然后調用resolveDependencies()方法解決依賴,實例化所有依賴類對象,最后就是創建對象了。

       

      posted @ 2017-09-14 13:10  瘋一樣的狼人  閱讀(4823)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲熟妇色自偷自拍另类| 国产人妻精品午夜福利免费| 四虎精品寂寞少妇在线观看| 国产91成人亚洲综合在线| 又黄又无遮挡AAAAA毛片| 日韩精品 在线一区二区| 亚洲av产在线精品亚洲第一站| 亚洲av专区一区| 国精品无码一区二区三区在线| 国产精品乱一区二区三区| 免费的很黄很污的视频| 9l精品人妻中文字幕色| 国产精品中文字幕久久| 在线a级毛片无码免费真人| 给我播放片在线观看| 亚洲精品天堂在线观看| 亚洲精品国产精品国在线| 久久66热人妻偷产精品| 国产美女在线观看大长腿| 精品人妻中文字幕av| 国产一区二区三区精美视频 | 亚洲欧美国产精品专区久久| 亚洲精品国产一区二区三区在线观看 | 国产嫩草精品网亚洲av| 亚洲人成在久久综合网站| 欧美日韩精品一区二区三区高清视频 | 嘉荫县| 亚洲精品人妻中文字幕| 日韩激情一区二区三区| 永春县| 国产国语毛片在线看国产| 国产精品一线二线三线区| 日韩少妇人妻vs中文字幕| 国产亚洲999精品AA片在线爽| 97久久精品无码一区二区| 屏南县| 不卡无码人妻一区三区音频| 四虎影视一区二区精品| 亚洲综合成人av在线| 99久久99久久久精品久久| 国产成AV人片久青草影院|