前言
通過實(shí)現(xiàn)laravel 框架功能宙攻,以便深入理解laravel框架的先進(jìn)思想助被。
什么是服務(wù)容器
服務(wù)容器是用來管理類依賴與運(yùn)行依賴注入的工具。Laravel框架中就是使用服務(wù)容器來實(shí)現(xiàn) ** 控制反轉(zhuǎn) ** 和 ** 依賴注入 **竟稳。
什么是控制反轉(zhuǎn)(IoC)和依賴注入(DI)
控制反轉(zhuǎn)(IoC) 就是說把創(chuàng)建對(duì)象的** 控制權(quán) **進(jìn)行轉(zhuǎn)移健蕊,以前創(chuàng)建對(duì)象的主動(dòng)權(quán)和創(chuàng)建時(shí)機(jī)是由自己把控的,而現(xiàn)在這種權(quán)力轉(zhuǎn)移到第三方罩缴,也就是 ** Laravel ** 中的容器蚊逢。
依賴注入(DI)則是幫助容器實(shí)現(xiàn)在運(yùn)行中動(dòng)態(tài)的為對(duì)象提供提依賴的資源层扶。
概念容易不太容易讓人理解,舉個(gè)栗子:
//我們構(gòu)建一個(gè)人的類和一個(gè)狗的類
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 '汪汪汪';
}
}
這個(gè)人在遛狗烙荷,突然遇到了死對(duì)頭镜会,他于是放狗咬人
$people = new People();
$people->putDog();
在這個(gè)操作中,people類要執(zhí)行putDog()
這個(gè)方法终抽,需要依賴Dog類戳表,一般我們像上面一樣,在people中利用構(gòu)造函數(shù)來添加這個(gè)Dog依賴昼伴。如果使用控制反轉(zhuǎn) 依賴注入則是這個(gè)樣子
class People
{
public $dog = null;
public function __construct(Dog $dog)
{
$this->dog = $dog;
}
public function putDog(){
return $this->dog->dogCall();
}
}
People類通過構(gòu)造參數(shù)聲明自己需要的 依賴類匾旭,由容器自動(dòng)注入。這樣就實(shí)現(xiàn)了程序的有效解耦圃郊,好處在這就不多說了价涝。
Laravel容器依賴注入的實(shí)現(xiàn)
實(shí)現(xiàn)原理需要了解的知識(shí)點(diǎn):
閉包(匿名函數(shù)):
匿名函數(shù)(Anonymous functions),也叫閉包函數(shù)(closures)持舆,允許 臨時(shí)創(chuàng)建一個(gè)沒有指定名稱的函數(shù)
反射:PHP 5 以上版本具有完整的反射 API色瘩,添加了對(duì)類、接口吏廉、函數(shù)泞遗、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外席覆,反射 API 提供了方法來取出函數(shù)史辙、類和方法中的文檔注釋
理解了閉包和反射的基本用法我們來看Laravel中是怎么實(shí)現(xiàn)容器的,下面代碼是我對(duì)laravel框架容器部分代碼的簡(jiǎn)化核心版:
class Containerc
{
/**
* 容器綁定佩伤,用來裝提供的實(shí)例或者 提供實(shí)例的回調(diào)函數(shù)
* @var array
*/
public $building = [];
/**
* 注冊(cè)一個(gè)綁定到容器
*/
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");
}
//注冊(cè)一個(gè)共享的綁定 單例
public function singleton($abstract, $concrete, $shared = true){
$this->bind($abstract, $concrete, $shared);
}
/**
* 默認(rèn)生成實(shí)例的回調(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);
};
}
/**
* 生成實(shí)例
*/
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ù)實(shí)體
*/
public function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}
/**
* 根據(jù)實(shí)例具體名稱實(shí)例具體對(duì)象
*/
public function build($concrete)
{
if($concrete instanceof Closure){
return $concrete($this);
}
//創(chuàng)建反射對(duì)象
$reflector = new ReflectionClass($concrete);
if( ! $reflector->isInstantiable()){
//拋出異常
throw new \Exception('無法實(shí)例化');
}
$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;
}
//解決一個(gè)沒有類型提示依賴
public function resolvedNonClass(ReflectionParameter $parameter)
{
if($parameter->isDefaultValueAvailable()){
return $parameter->getDefaultValue();
}
throw new \Exception('出錯(cuò)');
}
//通過容器解決依賴
public function resolvedClass(ReflectionParameter $parameter)
{
return $this->make($parameter->getClass()->name);
}
}
容器的工作流程
接著上面遛狗的例子:
//實(shí)例化容器類
$app = new Container();
//向容器中填充Dog
$app->bind('Dog','App\Dog');
//填充People
$app->bind('People', 'App\People');
//通過容器實(shí)現(xiàn)依賴注入聊倔,完成類的實(shí)例化;
$people = $app->make('People');
//調(diào)用方法
echo $people->putDog();
上面示例中我們先實(shí)例化容器類生巡,然后使用bind()
方法 綁定接口和 生成相應(yīng)的實(shí)例的閉包函數(shù)耙蔑。然后使用make()
函數(shù)生成實(shí)例對(duì)象,在make()
中會(huì)調(diào)用 isBuildable($concrete, $abstract)
來判斷 給定的服務(wù)實(shí)體($concrete
參數(shù))是否可以創(chuàng)建孤荣,可以創(chuàng)建 就會(huì)調(diào)用 build($concrete)
函數(shù) 甸陌,build($concrete)
函數(shù)會(huì)判斷傳的參數(shù)是 是** 閉包 還是 具體類名 **,如果是閉包則直接運(yùn)行盐股,如果是具體類名的話钱豁,則通過反射獲取該類的構(gòu)造函數(shù)所需的依賴,完成實(shí)例化疯汁。
** 重點(diǎn)理解 下面這幾個(gè)函數(shù)中 反射的用法牲尺,應(yīng)該就很好理解了 **
build($concrete)
getDependencies(array $dependencies)
resolvedNonClass(ReflectionParameter $parameter)
resolvedClass(ReflectionParameter $parameter) ```
## 最后 ##
> IoC 理解起來是有點(diǎn)難度,可能文中描述讓你感覺不是很清楚幌蚊,可以將文中代碼 在php中用debug觀察 運(yùn)行狀態(tài)谤碳。
理解了容器的具體實(shí)現(xiàn)原理溃卡,再去看Laravel中的相關(guān)實(shí)現(xiàn),就會(huì)感覺豁然開朗蜒简。