摘要:哲學的一個重要組成部分就是容器,也可以稱為服務容器。那我們要怎么做呢請看下面的例子數據庫連接通過上面的代碼,如果我們想把改成,根本不需要去修改類構造函數里的依賴。現在我要講下容器里到底發生了什么。
IOC 容器是一個實現依賴注入的便利機制 - Taylor?Otwell文章轉自:https://learnku.com/laravel/t...
Laravel 是當今最流行、最常使用的開源現代 web 應用框架之一。它提供了一些獨特的特性,比如 Eloquent ORM, Query 構造器,Homestead 等時髦的特性,這些特性只有 Laravel 中才有。
我喜歡 Laravel 是由于它猶如建筑風格一樣的獨特設計。Laravel 的底層使用了多設計模式,比如單例、工廠、建造者、門面、策略、提供者、代理等模式。隨著本人知識的增長,我越來越發現 Laravel 的美。Laravel 為開發者減少了苦惱,帶來了更多的便利。
學習 Laravel,不僅僅是學習如何使用不同的類,還要學習 Laravel 的哲學,學習它優雅的語法。Laravel 哲學的一個重要組成部分就是 IoC 容器,也可以稱為服務容器。它是一個 Laravel 應用的核心部分,因此理解并使用 IoC 容器是我們必須掌握的一項重要技能。
IoC 容器是一個非常強大的類管理工具。它可以自動解析類。接下來我會試著去說清楚它為什么如此重要,以及它的工作原理。
首先,我想先談下依賴反轉原則,對它的了解會有助于我們更好地理解 IoC 容器的重要性。
該原則規定:
高層次的模塊不應該依賴于低層次的模塊,兩者都應該依賴于抽象接口。抽象接口不應該依賴于具體實現。而具體實現則應該依賴于抽象接口。
一言以蔽之: 依賴于抽象而非具體
class MySQLConnection { /** * 數據庫連接 */ public function connect() { var_dump(‘MYSQL Connection’); } } class PasswordReminder { /** * @var MySQLConnection */ private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; } }
大家常常會有一個誤解,那就是依賴反轉就只是依賴注入的另一種說法。但其實二者是不同的。在上面的代碼示例中,盡管在 PasswordReminder 類中注入了 MySQLConnection 類,但它還是依賴于 MySQLConnection 類。
然而,高層次模塊 PasswordReminder 是不應該依賴于低層次模塊 MySQLConnection 的。
如果我們想要把 MySQLConnection 改成 MongoDBConnection,那我們就還得手動修改 PasswordReminder 類構造函數里的依賴。
PasswordReminder 類應該依賴于抽象接口,而非具體類。那我們要怎么做呢?請看下面的例子:
interface ConnectionInterface { public function connect(); } class DbConnection implements ConnectionInterface { /** * 數據庫連接 */ public function connect() { var_dump(‘MYSQL Connection’); } } class PasswordReminder { /** * @var DBConnection */ private $dbConnection; public function __construct(ConnectionInterface $dbConnection) { $this->dbConnection = $dbConnection; } }
通過上面的代碼,如果我們想把 MySQLConnection 改成 MongoDBConnection,根本不需要去修改 PasswordReminder 類構造函數里的依賴。因為現在 PasswordReminder 類依賴的是接口,而非具體類。
如果你對接口的概念還不是很了解,可以看下?這篇文章?。它會幫助你理解依賴反轉原則和 IoC 容器等。
現在我要講下 IoC 容器里到底發生了什么。我們可以把 IoC 容器簡單地理解為就是一個容器,里面裝的是類的依賴。
OrderRepositoryInterface 接口:
namespace AppRepositories; interface OrderRepositoryInterface { public function getAll(); }
DbOrderRepository 類:
namespace AppRepositories; class DbOrderRepository implements OrderRepositoryInterface { function getAll() { return "Getting all from mysql"; } }
OrdersController 類:
namespace AppHttpControllers; use IlluminateHttpRequest; use AppHttpRequests; use AppRepositoriesOrderRepositoryInterface; class OrdersController extends Controller { protected $order; function __construct(OrderRepositoryInterface $order) { $this->order = $order; } public function index() { dd($this->order->getAll()); return View::make(orders.index); } }
路由:
Route::resource("orders", "OrdersController");
現在,在瀏覽器中輸入這個地址 ?
報錯了吧,錯誤的原因是服務容器正在嘗試去實例化一個接口,而接口是不能被實例化的。解決這個問題,只需把接口綁定到一個具體的類上:
把下面這行代碼加在路由文件里就搞定了:
App::bind("AppRepositoriesOrderRepositoryInterface", "AppRepositoriesDbOrderRepository");
現在刷新瀏覽器看看:
我們可以這樣定義一個容器類:
class SimpleContainer { protected static $container = []; public static function bind($name, Callable $resolver) { static::$container[$name] = $resolver; } public static function make($name) { if(isset(static::$container[$name])){ $resolver = static::$container[$name] ; return $resolver(); } throw new Exception("Binding does not exist in containeer"); } }
這里,我想告訴你服務容器解析依賴是多么簡單的事。
class LogToDatabase { public function execute($message) { var_dump("log the message to a database :".$message); } } class UsersController { protected $logger; public function __construct(LogToDatabase $logger) { $this->logger = $logger; } public function show() { $user = "JohnDoe"; $this->logger->execute($user); } }
綁定依賴:
SimpleContainer::bind("Foo", function() { return new UsersController(new LogToDatabase); }); $foo = SimpleContainer::make("Foo"); print_r($foo->show());
輸出:
string(36) "Log the messages to a file : JohnDoe"
Laravel 的服務容器源碼:
public function bind($abstract, $concrete = null, $shared = false) { $abstract = $this->normalize($abstract); $concrete = $this->normalize($concrete); if (is_array($abstract)) { list($abstract, $alias) = $this->extractAlias($abstract); $this->alias($abstract, $alias); } $this->dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; } if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact("concrete", "shared"); if ($this->resolved($abstract)) { $this->rebound($abstract); } } public function make($abstract, array $parameters = []) { $abstract = $this->getAlias($this->normalize($abstract)); if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete, $parameters); } else { $object = $this->make($concrete, $parameters); } foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } if ($this->isShared($abstract)) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); $this->resolved[$abstract] = true; return $object; } public function build($concrete, array $parameters = []) { if ($concrete instanceof Closure) { return $concrete($this, $parameters); } $reflector = new ReflectionClass($concrete); if (! $reflector->isInstantiable()) { if (! empty($this->buildStack)) { $previous = implode(", ", $this->buildStack); $message = "Target [$concrete] is not instantiable while building [$previous]."; } else { $message = "Target [$concrete] is not instantiable."; } throw new BindingResolutionException($message); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); $parameters = $this->keyParametersByArgument( $dependencies, $parameters ); $instances = $this->getDependencies($dependencies,$parameters); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }
如果你想了解關于服務容器的更多內容,可以看下 ?vendor/laravel/framwork/src/Illuminate/Container/Container.php
簡單的綁定
$this->app->bind("HelpSpotAPI", function ($app) { return new HelpSpotAPI($app->make("HttpClient")); });
單例模式綁定
通過 singleton?方法綁定到服務容器的類或接口,只會被解析一次。
$this->app->singleton("HelpSpotAPI", function ($app) { return new HelpSpotAPI($app->make("HttpClient")); });
綁定實例
也可以通過 instance 方法把具體的實例綁定到服務容器中。之后,就會一直返回這個綁定的實例:
$api = new HelpSpotAPI(new HttpClient); $this->app->instance("HelpSpotAPI", $api);
如果沒有綁定,PHP 會利用反射機制來解析實例和依賴。
如果想了解更多細節,可以查看?官方文檔
關于 Laravel 服務容器的練習代碼, 可以從我的 ?GitHub?(如果喜歡,煩請不吝 star )倉庫獲取。
感謝閱讀。
文章轉自:https://learnku.com/laravel/t...
更多文章:https://learnku.com/laravel/c...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31261.html
摘要:依賴注入依賴注入一詞是由提出的術語,它是將組件注入到應用程序中的一種行為。就像說的依賴注入是敏捷架構中關鍵元素。類依賴于,所以我們的代碼可能是這樣的創建一個這是一種經典的方法,讓我們從使用構造函數注入開始。 showImg(https://segmentfault.com/img/remote/1460000018806800); 文章轉自:https://learnku.com/la...
摘要:工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。其實我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。雖然如此,工廠模式依舊十分優秀,并且適用于絕大多數情況。 此篇文章轉載自laravel-china,chongyi的文章https://laravel-china.org/top...原文地址: http://www.insp.top/learn-lar... ,轉載務必保...
摘要:本文一大半內容都是通過舉例來讓讀者去理解什么是控制反轉和依賴注入,通過理解這些概念,來更加深入。這種由外部負責其依賴需求的行為,我們可以稱其為控制反轉。工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當然,有這樣一種容器,它存放的不是文本、數值,而是對象、對象的描...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:簡述的生命周期采用了單一入口模式,應用的所有請求入口都是文件。分發請求一旦應用完成引導和所有服務提供者都注冊完成,將會移交給路由進行分發。此外,由于對動態方法的獨特用法,也使測試起來非常容易。 本書的 GitHub 地址:https://github.com/todayqq/PH... Laravel 作為現在最流行的 PHP 框架,其中的知識較多,所以單獨拿出來寫一篇。 簡述 La...
閱讀 2484·2023-04-25 19:24
閱讀 1700·2021-11-11 16:54
閱讀 2833·2021-11-08 13:19
閱讀 3547·2021-10-25 09:45
閱讀 2552·2021-09-13 10:24
閱讀 3276·2021-09-07 10:15
閱讀 4014·2021-09-07 10:14
閱讀 2950·2019-08-30 15:56