前言
通過實現(xiàn)laravel 框架功能,以便深入理解laravel框架的先進思想。
什么是服務(wù)容器
服務(wù)容器是用來管理類依賴與運行依賴注入的工具。Laravel框架中就是使用服務(wù)容器來實現(xiàn) ** 控制反轉(zhuǎn) ** 和 ** 依賴注入 **。
什么是控制反轉(zhuǎn)(IoC)和依賴注入(DI)
控制反轉(zhuǎn)(IoC) 就是說把創(chuàng)建對象的** 控制權(quán) 進行轉(zhuǎn)移,以前創(chuàng)建對象的主動權(quán)和創(chuàng)建時機是由自己把控的,而現(xiàn)在這種權(quán)力轉(zhuǎn)移到第三方,也就是Laravel ** 中的容器。
依賴注入(DI)則是幫助容器實現(xiàn)在運行中動態(tài)的為對象提供提依賴的資源。
概念容易不太容易讓人理解,舉個栗子:
//我們構(gòu)建一個人的類和一個狗的類 class People{ public $dog = null; public function __construct() { $this->dog = new Dog(); } public function putDog(){ return $this->dog->dogCall(); } }class Dog{ public function dogCall(){ return '汪汪汪'; } } ``` 這個人在遛狗,突然遇到了死對頭,他于是放狗咬人
$people = new People();
$people->putDog();在這個操作中,people類要執(zhí)行
putDog() 這個方法,需要依賴Dog類,一般我們像上面一樣,在people中利用構(gòu)造函數(shù)來添加這個Dog依賴。如果使用控制反轉(zhuǎn) 依賴注入則是這個樣子
class People
{
public $dog = null;
public function __construct(Dog $Dog){ $this->dog = $dog; }public function putDog(){ return $this->dog->dogCall(); }
}
People類通過構(gòu)造參數(shù)聲明自己需要的 依賴類,由容器自動注入。這樣就實現(xiàn)了程序的有效解耦,好處在這就不多說了。## Laravel容器依賴注入的實現(xiàn)###### 實現(xiàn)原理需要了解的知識點:> 閉包(匿名函數(shù)):匿名函數(shù)(Anonymous functions),也叫閉包函數(shù)(closures),允許 臨時創(chuàng)建一個沒有指定名稱的函數(shù)> 反射:PHP 5 以上版本具有完整的反射 API,添加了對類、接口、函數(shù)、方法和擴展進行反向工程的能力。 此外,反射 API 提供了方法來取出函數(shù)、類和方法中的文檔注釋###### 理解了閉包和反射的基本用法我們來看Laravel中是怎么實現(xiàn)容器的,下面代碼是我對laravel框架容器部分代碼的簡化核心版:
lass Container
{
/**
* 容器綁定,用來裝提供的實例或者 提供實例的回調(diào)函數(shù)
* @var array
*/
public $building = [];
/** * 注冊一個綁定到容器 */public function bind($abstract, $concrete = null, $shared = false){ if(is_null($concrete)){ $concrete = $abstract; } if(!$concrete instanceOf Closure){ $concrete = $this->getClosure($abstract, $concrete); } $this->building[$abstract] = compact("concrete", "shared"); }//注冊一個共享的綁定 單例public function singleton($abstract, $concrete, $shared = true){ $this->bind($abstract, $concrete, $shared); }/** * 默認(rèn)生成實例的回調(diào)閉包 * * @param $abstract * @param $concrete * @return Closure */public function getClosure($abstract, $concrete){ return function($c) use($abstract, $concrete){ $method = ($abstract == $concrete)? 'build' : 'make'; return $c->$method($concrete); }; }/** * 生成實例 */public function make($abstract){ $concrete = $this->getConcrete($abstract); if($this->isBuildable($concrete, $abstract)){ $object = $this->build($concrete); }else{ $object = $this->make($concrete); } return $object; }/** * 獲取綁定的回調(diào)函數(shù) */public function getConcrete($abstract){ if(! isset($this->building[$abstract])){ return $abstract; } return $this->building[$abstract]['concrete']; }/** * 判斷 是否 可以創(chuàng)建服務(wù)實體 */public function isBuildable($concrete, $abstract){ return $concrete === $abstract || $concrete instanceof Closure; }/** * 根據(jù)實例具體名稱實例具體對象 */public function build($concrete){ if($concrete instanceof Closure){ return $concrete($this); } //創(chuàng)建反射對象 $reflector = new ReflectionClass($concrete); if( ! $reflector->isInstantiable()){ //拋出異常 throw new \Exception('無法實例化'); } $constructor = $reflector->getConstructor(); if(is_null($constructor)){ return new $concrete; } $dependencies = $constructor->getParameters(); $instance = $this->getDependencies($dependencies); return $reflector->newInstanceArgs($instance); }//通過反射解決參數(shù)依賴public function getDependencies(array $dependencies){ $results = []; foreach( $dependencies as $dependency ){ $results[] = is_null($dependency->getClass()) ?$this->resolvedNonClass($dependency) :$this->resolvedClass($dependency); } return $results; }//解決一個沒有類型提示依賴public function resolvedNonClass(ReflectionParameter $parameter){ if($parameter->isDefaultValueAvailable()){ return $parameter->getDefaultValue(); } throw new \Exception('出錯'); }//通過容器解決依賴public function resolvedClass(ReflectionParameter $parameter){ return $this->make($parameter->getClass()->name); }
}
```
容器的工作流程
接著上面遛狗的例子://實例化容器類 $app = new Container(); //向容器中填充Dog $app->bind('Dog','App\Dog'); //填充People $app->bind('People', 'App\People'); //通過容器實現(xiàn)依賴注入,完成類的實例化; $people = $app->make('People'); //調(diào)用方法 echo $people->putDog();
上面示例中我們先實例化容器類,然后使用bind()
方法 綁定接口和 生成相應(yīng)的實例的閉包函數(shù)。然后使用make()
函數(shù)生成實例對象,在make()
中會調(diào)用 isBuildable($concrete, $abstract)
來判斷 給定的服務(wù)實體($concrete
參數(shù))是否可以創(chuàng)建,可以創(chuàng)建 就會調(diào)用 build($concrete)
函數(shù) ,build($concrete)
函數(shù)會判斷傳的參數(shù)是 是** 閉包 還是 具體類名 **,如果是閉包則直接運行,如果是具體類名的話,則通過反射獲取該類的構(gòu)造函數(shù)所需的依賴,完成實例化。
** 重點理解 下面這幾個函數(shù)中 反射的用法,應(yīng)該就很好理解了 **build($concrete) getDependencies(array $dependencies) resolvedNonClass(ReflectionParameter $parameter) resolvedClass(ReflectionParameter $parameter)
最后
IoC 理解起來是有點難度,可能文中描述讓你感覺不是很清楚,可以將文中代碼 在php中用debug觀察 運行狀態(tài)。
理解了容器的具體實現(xiàn)原理,再去看Laravel中的相關(guān)實現(xiàn),就會感覺豁然開朗。
http://www.cnblogs.com/lilili/p/6953749.html