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