Laravel 核心理解 - IoC容器 ServiceContainer

Laravel Core Theories

官方文檔項(xiàng)目 https://github.com/laravel/docs
providers.md https://laravel.com/docs/5.6/providers
container.md https://laravel.com/docs/5.6/container

Laravel 的核心就是一個(gè) IoC 容器,該容器提供了整個(gè)框架中需要的一系列服務(wù)。在官方架構(gòu)概念文檔 Architecture Concepts 中提到五個(gè)重點(diǎn):

? Request Lifecycle
? Service Container
? Service Providers
? Facades
? Contracts

核心的概念有 Service Container、Service ProviderFacades 以及 Contracts,這幾個(gè)核心的概念相互交織,協(xié)同工作,構(gòu)成了 Laravel 開(kāi)發(fā)框架的基石,讓我們能夠更高效的進(jìn)行 Larave 開(kāi)發(fā)。

? Facades 外觀模式

相當(dāng)代理模式,從偷懶的角度,別名而已,少敲幾個(gè)字。另外一個(gè)好處是方便的自定義,直接修改 config/app.php 里的 aliases 部分即可。在 Laravel 里的作用其實(shí)就跟 PHP 的 class_alias 的作用類似,把框架里帶命名空間 namespace 的類變得更好記,比如:

App  => Illuminate\Support\Facades\App

在 Laravel 應(yīng)用中,一個(gè) facade 就是一個(gè)提供訪問(wèn)容器中對(duì)象的類,實(shí)現(xiàn) Illuminate\Support\Facades\Facade 基礎(chǔ)抽象類。Facade 基類使用 __callStatic() 魔術(shù)方法在你的 Facades 中延遲調(diào)用容器中對(duì)應(yīng)對(duì)象的方法。

當(dāng)然外觀還有一些其它的好處,也有它的弊端,比如無(wú)法直接訪問(wèn)真正目標(biāo)對(duì)象的類常量,因?yàn)?Facades 是通過(guò) PHP 的 __callStatic() 這個(gè)魔術(shù)方法實(shí)現(xiàn)的。

? Contracts 鍥約

是指框架提供的一系列定義核心服務(wù)的接口,比如緩存,隊(duì)列,日志的接口,契約就是接口 interface。它用來(lái)實(shí)現(xiàn)松耦合,當(dāng)系統(tǒng)升級(jí),需要對(duì)一種實(shí)現(xiàn)進(jìn)行修改時(shí),如 Memcached 需要升級(jí)到 Redis,能夠不對(duì)代碼庫(kù)進(jìn)行修改,只需要對(duì)配置進(jìn)行修改就能完成升級(jí)的時(shí)候就算是松耦合了!

? Service Container

IoC(Inversion Of Control)控制反轉(zhuǎn),是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,可以用來(lái)減低計(jì)算機(jī)代碼之間的耦合度。IoC 容器則是實(shí)現(xiàn)這種設(shè)計(jì)的一種模式,F(xiàn)actory 模式也能實(shí)現(xiàn) IoC。

IoC 容器會(huì)根據(jù)類的依賴需求,自動(dòng)在注冊(cè)、綁定的一堆實(shí)例中搜尋符合的依賴需求,并自動(dòng)注入到構(gòu)造函數(shù)參數(shù)中去。通過(guò)控制反轉(zhuǎn),對(duì)象在被創(chuàng)建的時(shí)候,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對(duì)象的外界實(shí)體,將其所依賴的對(duì)象的引用傳遞給它。也可以說(shuō),依賴被注入到對(duì)象中。這就是依賴注入(Dependency Injection, DI)。依賴注入實(shí)質(zhì)上是指:類的依賴通過(guò)構(gòu)造器或在某些情況下通過(guò)「setter」方法進(jìn)行「注入」。這里結(jié)合 TypeScript 列子來(lái)解釋:

class World {
    dog = new Dog();
    run(){
        this.dog.barking();
    }
}

class Dog {
    barking(){
        console.log("Wang...Wang...Wang...Wang...");
    }
}

new World().run();

這段代碼模擬了一個(gè)世界,世界運(yùn)行時(shí)會(huì)有一條狗在吠?,F(xiàn)在想改一下需求,增加一只貓,讓它跳一跳。對(duì)于這個(gè)列子,需要修改核心世界的代碼,這就時(shí)高耦合的體現(xiàn)。如果這個(gè)世界就是一個(gè)框架,那么開(kāi)發(fā)者就不能再不修改核心的情況下進(jìn)行擴(kuò)展。這就是高耦合的致命缺點(diǎn),現(xiàn)在引入 IoC 模式來(lái)改造它,依賴遷移到外部,核心部分負(fù)責(zé)指定接口規(guī)范:

class World {
    dog = new Dog();
    run( it:Animal ){
        console.log(it.type);
        it.act();
        return this;
    }
}

interface Animal {
    readonly type:String;
    act():boolean;
}

class Dog implements Animal {
    type = "Dog";
    act(){
        console.log("Wang...Wang...");
        return true;
    }
}

class Cat implements Animal {
    type = "Cat";
    act(){
        console.log("Jump...");
        return true;
    }
}

new World().run(new Dog()).run(new Cat());

通過(guò)改造,核心部分包括了 World 框架和 Animal 接口規(guī)范,而 DogCat 則需要按規(guī)范進(jìn)行擴(kuò)展,只要遵循這個(gè)接口規(guī)范即可。當(dāng)然 Laravel 的 IoC 容器設(shè)計(jì)沒(méi)有這么簡(jiǎn)單,它還使用了 PHP 的反射技術(shù)。

對(duì)于每一個(gè) ServiceProvider ,框架運(yùn)行時(shí)會(huì)調(diào)用 register() 方法完成該服務(wù)的注冊(cè),然后執(zhí)行引導(dǎo)方法 boot()。

ServiceProvider 內(nèi)部通過(guò) $this->app 來(lái)訪問(wèn)整個(gè)應(yīng)用的服務(wù)容器實(shí)例,通過(guò)服務(wù)容器 bind() 方法綁定 ServiceProvider 或接口,其它綁定方式有 instance() 實(shí)例、singleton()等。

$this->app->bind('post', function ($app) {
    return new App\Models\Post;
});

以下兩行等效代碼實(shí)例化 ServiceProvider:

$app->make('App\Models\Post');
$app->make('post');

再實(shí)列化過(guò)程,resolve() 全局函數(shù)會(huì)負(fù)責(zé)定位到類定文件。通過(guò) PHP 反射技術(shù),Laravel 可以做自動(dòng)依賴注入,如在控制器的構(gòu)造函數(shù)中顯式聲明了參數(shù)的類型即具有 type-hit 類型提醒功能。容器通過(guò) resolve() 解析到它時(shí)就知道需要實(shí)例化什么類型的對(duì)象并傳入到構(gòu)造器中:

public function __construct(UserRepository $users)
{
    $this->users = $users;
}

可以自動(dòng)依賴注入的不局限于控制器,還有 event listeners, queue jobs, middleware 等等。

? Service Provider

服務(wù)提供者 Provider 就是實(shí)現(xiàn)依賴的部分,實(shí)現(xiàn) \Illuminate\Support\ServiceProvider 基礎(chǔ)抽象類,

register() 方法注入容器就實(shí)現(xiàn)了完整功能
boot() 引導(dǎo)方法也就是依賴注入方法

Laravel 中,包括應(yīng)用程序,以及所有的核心服務(wù),都是通過(guò) Provider 引導(dǎo)的。所謂的「引導(dǎo)」指的是注冊(cè)事務(wù),包括注冊(cè)服務(wù)容器綁定,事件監(jiān)聽(tīng)器,中間件,甚至路由。

所有服務(wù)提供者都在 config/app.php 配置文件中注冊(cè)??梢赃x擇推遲服務(wù)提供者的注冊(cè),直到真正需要注冊(cè)綁定時(shí),這樣可以提供應(yīng)用程序的性能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容