摘要:外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。將使用者與子系統(tǒng)從直接耦合,轉(zhuǎn)變成由外觀類提供統(tǒng)一的接口給使用者使用,以降低客戶端與子系統(tǒng)之間的耦合度。接下來將深入分析外觀服務(wù)的加載過程。引導(dǎo)程序?qū)⒃谔幚碚埱笫峭瓿梢龑?dǎo)啟動。
本文首發(fā)于 深入淺出 Laravel 的 Facade 外觀系統(tǒng),轉(zhuǎn)載請注明出處。
今天我們將學(xué)習(xí) Laravel 核心架構(gòu)中的另一個主題「Facade(外觀)」。
本文將從以下幾個方面出發(fā),全面講解 Laravel 中 Facade 的運行原理,為了便于理解后續(xù)中所有 Facade 譯作「外觀」:
簡單介紹「外觀」設(shè)計模式;
Laravel「外觀」的加載原理;
Laravel「外觀」基本使用。
什么是「外觀」設(shè)計模式 外觀模式定義為子系統(tǒng)中的一組接口提供一個統(tǒng)一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。
外觀模式是一種使用頻率非常高的結(jié)構(gòu)型設(shè)計模式,它通過引入一個外觀角色來簡化客戶端與子系統(tǒng)之間的交互,
為復(fù)雜的子系統(tǒng)調(diào)用提供一個統(tǒng)一的入口,降低子系統(tǒng)與客戶端的耦合度,且客戶端調(diào)用非常方便。 - 設(shè)計模式 Java 版
核心 就是在 客戶端(使用者) 與 子系統(tǒng)(接口或服務(wù)) 之間引入一個「外觀」角色。
將使用者與子系統(tǒng)從直接耦合,轉(zhuǎn)變成由「外觀」類提供統(tǒng)一的接口給使用者使用,以降低客戶端與子系統(tǒng)之間的耦合度。
結(jié)構(gòu)示意圖:關(guān)于「外觀模式」可以閱讀 設(shè)計模式 Java 版 - 外觀模式
Laravel 外觀組件Laravel 中的「外觀」組件實際上是服務(wù)容器中底層類的「靜態(tài)代理」,它將 Laravel 內(nèi)核中定義的「Contracts(在 Laravel 中又
稱為服務(wù)、契約或者通常我們所說的接口)」,以靜態(tài)可調(diào)用的方式封裝到各個「外觀」服務(wù)中供我們使用。
在講解如何使用外觀組件之前,我們依舊先去深入分析「外觀」組件是如何被 Laravel 加載到項目中的。這一步是
用好「外觀」組件的前提。
所有內(nèi)置的外觀組件的配置數(shù)據(jù),同 Laravel 其它服務(wù)一樣被定義在 config/app.php 文件中。讓我們來瀏覽一下 aliases 節(jié)點的配置數(shù)據(jù)吧:
... "aliases" => [ "App" => IlluminateSupportFacadesApp::class, "Artisan" => IlluminateSupportFacadesArtisan::class, ... ], ...
外觀配置定義格式遵循 「別名」:「外觀類」 的數(shù)據(jù)格式。當一個 HTTP 請求被接收時,將在處理請求階段將這些「外觀」組件加載到服務(wù)中。
接下來將深入分析外觀服務(wù)的加載過程。
加載外觀服務(wù)「外觀」服務(wù)的加載工作由定義在 IlluminateFoundationHttpKernel 內(nèi)核中的 IlluminateFoundationBootstrapRegisterFacades::class 啟動程序完成。
引導(dǎo)啟動外觀服務(wù)如果你已經(jīng)閱讀我的另一篇文章 深入剖析 Laravel 服務(wù)提供者實現(xiàn)原理,你應(yīng)該對引導(dǎo)程序不會太陌生。
引導(dǎo)程序?qū)⒃谔幚?HTTP 請求是完成引導(dǎo)啟動 bootstrap()。所以這里我們需要深入到 RegisterFacades 類的內(nèi)部去了解更多細節(jié)上的處理。
make("config")->get("app.aliases", []), $app->make(PackageManifest::class)->aliases() ))->register(); } }
加載外觀服務(wù)有 AliasLoader 組件完成:
首先,會從配置文件 config/app.php 中讀取所有的「外觀」服務(wù)配置 aliases;
再從清單文件中讀取別名服務(wù) $app->make(PackageManifest::class)->aliases();
將兩個配置數(shù)組合并后注入到 AliasLoader 完成 注冊(register)。
注冊外觀服務(wù)最后我們來瞧瞧 AliasLoader 加載器是如何將所有的「外觀」服務(wù)加載到系統(tǒng)中的。
getAliases(), $aliases); static::$instance->setAliases($aliases); return static::$instance; } /** * Set the registered aliases. 設(shè)置需注冊別名數(shù)據(jù)。 */ public function setAliases(array $aliases) { $this->aliases = $aliases; } /** * Register the loader on the auto-loader stack. 將加載器注冊到自動加載中。 */ public function register() { if (! $this->registered) { $this->prependToLoaderStack(); $this->registered = true; } } /** * Prepend the load method to the auto-loader stack. 設(shè)置自動加載方法。 */ protected function prependToLoaderStack() { // 將 AliasLoader 的 load 方法作為 __autoload 的實現(xiàn) spl_autoload_register([$this, "load"], true, true); } /** * Load a class alias if it is registered.從注冊過的服務(wù)中加載這個「外觀」服務(wù)。 */ public function load($alias) { if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) { $this->loadFacade($alias); return true; } if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } } }
注意 這里是知識點,在 AliasLoader->register() 完成「外服服務(wù)注冊」涉及 PHP 兩個知識的應(yīng)用:
PHP 內(nèi)置魔術(shù)方法 __autoload 的使用;
PHP 如何給類創(chuàng)建別名。
? 1. 外觀服務(wù)的動態(tài)引入
我們知道 __autoload 魔術(shù)方法的作用是嘗試加載未經(jīng)定義的類,這樣當我們使用一個未經(jīng)引入的類時,則會自動的給我們引入這個類。
更優(yōu)的解決方案是通過 spl_autoload_register 函數(shù),將自定義的類加載程序作為 __autoload 的實現(xiàn),以替代默認 __autoload() 模式函數(shù)或方法的行為。
所有 prependToLoaderStack() 方法:
/** * Prepend the load method to the auto-loader stack. 設(shè)置自動加載方法。 */ protected function prependToLoaderStack() { // 將 AliasLoader 的 load 方法作為 __autoload 的實現(xiàn) spl_autoload_register([$this, "load"], true, true); }
就是去完成這樣的作用,將 AliasLoader->load() 方法作為自動加載程序的實現(xiàn),在使用「外觀」服務(wù)時動態(tài)引入這個類。
? 2. 支持外觀服務(wù)別名
我們已經(jīng)了解到當「外觀」服務(wù)被使用時,由 AliasLoader->load() 去自動加載這個類。
與此同時,load 方法通過 class_alias($original, $alias) 函數(shù)完成別名注冊。
這樣,當我們使用 App 類時實際上就是在使用 IlluminateSupportFacadesApp 類。
很完美么,我們的「狗蛋」終于與「世界上最好的語言」畫上了等號。你就是我,我就是你。
到這里其實已經(jīng)完成了「外觀」服務(wù)工作原理分析工作的 70%。
探秘 Facade最后我們將揭開 Facade 的神秘面紗,研究一下 Laravel 是如何實現(xiàn) Facade 設(shè)計模式的。
我們拿 IlluminateSupportFacadesApp 外觀服務(wù)開刀,去解開類似 App::make() 靜態(tài)方法使用的奧秘。
深入 FacadesApp:
我們看到它的實現(xiàn)內(nèi)部僅僅定義了一個 getFacadeAccessor 方法,該方法的功能是獲取已注冊組件的名稱 app;除此之外,一無所有。
看來在這里我們得不到什么有用的信息了。繼續(xù)調(diào)查基類 IlluminateSupportFacadesFacade。如果你有去通便瀏覽全部的源碼。
$method(...$args); } }你會發(fā)現(xiàn)這個 Facade 基類并沒有定義類似 make 的方法,那么這里能夠靜態(tài)調(diào)用 App::make() 看來是需要從 __callStatic 著手才行。
不過在這里我們需要再次厘清一個事實:「外觀」模式的功能是什么?
將使用者與子系統(tǒng)從直接耦合,轉(zhuǎn)變成由「外觀」類提供統(tǒng)一的接口給使用者使用,以降低客戶端與子系統(tǒng)之間的耦合度。這句話的意思就是我「外觀」啥也不提供,就是一層對服務(wù)(或者說組件或接口)的封裝,然后以統(tǒng)一的方式提供給你們外部調(diào)用。
好了現(xiàn)在我們來看看 Facade::__callStatic 是如何獲取實際的服務(wù)并調(diào)用響應(yīng)的方法的吧。
首先,通過 getFacadeRoot 靜態(tài)方法獲取實際服務(wù)的實例對象;
然后,調(diào)用實例對象的相關(guān)方法并返回處理結(jié)果。
從 getFacadeRoot 解析對象的功能中我們可以看到:它會調(diào)用實現(xiàn)「外觀」的 getFacadeAccessor 方法獲取到組件(服務(wù)或者說接口)的名稱;然后從 Laravel 服務(wù)容器 static::$app[$name](app 是在 RegisterFacades 中注冊到「外觀」中) 中解析出相關(guān)服務(wù)。
到這里,我們就將「外觀」服務(wù)的基本工作原理給分析透徹了。
另外有關(guān)「外觀」組件的一些細枝末節(jié),如:
在文檔「Facades Vs. 輔助函數(shù)」一節(jié)提到的測試驗證是如何實現(xiàn)的 Cache::shouldReceive("get");
什么是「實時 Facades」。
還是需要你自行深入到 Facade 基類去一探究竟。
掃盲 ArrayAccess 接口另外補充一個知識點就是關(guān)于 static::$app[$name] 這一句代碼。你不經(jīng)要問,這有啥好補充的呢,不就是一個簡單獲取數(shù)據(jù)么。
獲取數(shù)據(jù)不假,簡單也不假。
不過你仔細看一下,你會發(fā)現(xiàn) static::$app 靜態(tài)成員變量難道不是一個 IlluminateContractsFoundationApplication 實現(xiàn)實例么,怎么可以從對象中以數(shù)組的方式獲取值呢?
這是因為我們的服務(wù)容器 IlluminateContainerContainer 實現(xiàn)了 ArrayAccess 接口。
該接口的功能是提供像訪問數(shù)組一樣訪問對象的能力的接口,這樣就可以像數(shù)組一樣訪問對象訪問成員。
/** *@link https://github.com/laravel/framework/blob/5.6/src/Illuminate/Container/Container.php */ class Container implements ArrayAccess, ContainerContract { /** * Get the value at a given offset. 獲取一個偏移位置的值,實際上從容器中解析出服務(wù)。 */ public function offsetGet($key) { return $this->make($key); } }Laravel「外觀」基本使用外觀服務(wù)的一個典型使用場景是在定義路由時使用 Route::get("/", ...)。這樣一看似乎「Laravel 別名服務(wù)」也就不這么神秘了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/28846.html
摘要:本文來自原文鏈接歡迎作客我們的學(xué)習(xí)群該篇屬于底層核心技術(shù)實戰(zhàn)揭秘這一課程底層核心概念解析這一章的擴展閱讀。考慮到學(xué)員們的基礎(chǔ)差異,為了避免視頻當中過于詳細而連篇累牘,故將一些底層實現(xiàn)相關(guān)的知識點以文章形式呈現(xiàn),供大家預(yù)習(xí)和隨時查閱。 本文來自pilishen.com----原文鏈接; 歡迎作客我們的php&Laravel學(xué)習(xí)群:109256050該篇屬于《Laravel底層核心技術(shù)實戰(zhàn)...
摘要:外觀模式的目的在于降低系統(tǒng)的復(fù)雜程度。在不引入抽象外觀類的情況下,增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼,違背了開閉原則。 外觀模式 外觀模式(Facade Pattern):外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的外觀對象進行,為子系統(tǒng)中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。外觀模式又稱為門面模式,它是一種對象結(jié)構(gòu)型模...
摘要:使用現(xiàn)在,在任何一個控制器,或者路由的回調(diào)函數(shù)中,使用你會發(fā)現(xiàn),已經(jīng)可以好好工作了,參考文章設(shè)計模式九外觀模式結(jié)構(gòu)型服務(wù)容器實例教程深入理解控制反轉(zhuǎn)和依賴注入服務(wù)提供者實例教程創(chuàng)建測試實例 我的博客原文: http://www.qinblog.net/Articl... 前言 laravel 提供了一個靈活的模式,那就是 facade ??蚣軆?nèi)部的 DB、Auth、File 等功能也...
摘要:沒有任何意外,王小二的公司用來開發(fā)公司的主打產(chǎn)品。臃腫的著手開干吧小二打開熟悉的,找到提交訂單模塊的。要不再去請教下哥的煩惱小二找到哥,詳細的描述了他的問題。 流行的MVC架構(gòu)模式 如今的Web開發(fā),各種框架風(fēng)起云涌,勢如破竹。 從國民第一的ThinkPhp到稱霸全球的Laravel,這些框架有一個共同特征,都采用了MVC的架構(gòu)模式。 showImg(https://segmentfa...
摘要:可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。所有服務(wù)提供者都在配置文件中注冊??梢赃x擇推遲服務(wù)提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應(yīng)用程序的性能。 本文最早發(fā)布于 Rootrl的Blog 導(dǎo)言 Laravel是一款先進的現(xiàn)代化框架,里面有一些概念非常重要。在上手Laravel之前,我認為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...
閱讀 1280·2021-09-27 13:35
閱讀 2581·2021-09-06 15:12
閱讀 3395·2019-08-30 15:55
閱讀 2853·2019-08-30 15:43
閱讀 444·2019-08-29 16:42
閱讀 3458·2019-08-29 15:39
閱讀 3075·2019-08-29 12:28
閱讀 1254·2019-08-29 11:11