laravel5.8 IoC 容器

網上 對容器的解釋有不少,這裏只是記錄,搬運!php

一、簡單理解:html

  

2019-10-10 11:24:09
解析 lavarel 容器

IoC 容器 做用 就是 「解耦」 、「依賴注入(DI)

IoC 容器,根據文檔,稱其爲 「服務容器」  主要存放 是對象、對象的描述(類、接口)或者是提供對象的回調
 

面向對象開發中依賴的產生和解決方法

IoC(控制反轉) 和 DI(依賴注入)

面向對象編程,有如下幾樣東西無時不刻的接觸:接口、類還有對象

這其中,接口是類的原型,一個類必需要遵照其實現的接口;對象則是一個類實例化後的產物,咱們稱其爲一個實例


工廠模式,顧名思義,就是一個類因此依賴的外部事物的實例,均可以被一個或多個 「工廠」 建立的這樣一種開發模式,就是 「工廠模式」。


只要不是由內部生產(好比初始化、構造函數 __construct 中經過工廠方法、自行手動 new 的),而是由外部以參數或其餘形式注入的,都屬於 依賴注入(DI)

更爲先進的工廠 —— IoC 容器


能夠說,laravel 的核心自己十分輕量,並無什麼很神奇很實質性的應用功能。不少人用到的各類功能模塊好比 Route(路由)、Eloquent ORM(數據庫 ORM 組件)、Request and Response(請求和響應)等等等等,實際上都是與核心無關的類模塊提供的,這些類從註冊到實例化,最終被你所使用,其實都是 laravel 的服務容器負責的


對,一個類要被容器所可以提取,必需要先註冊至這個容器。既然 laravel 稱這個容器叫作服務容器,那麼咱們須要某個服務,就得先註冊、綁定這個服務到容器,那麼提供服務並綁定服務至容器的東西,就是 服務提供者(ServiceProvider)


服務提供者主要分爲兩個部分,register(註冊) 和 boot(引導、初始化),具體參考文檔。register 負責進行向容器註冊 「腳本」,但要注意註冊部分不要有對未知事物的依賴,若是有,就要移步至 boot 部分。


們使用的 Route 類其實是 Illuminate\Support\Facades\Route 經過 class_alias() 函數創造的 別名 而已,這個類被定義在文件 vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php 。

bind 就是 實例化

make 傳參

Contracts(契約),也就是接口。定義一些方法,因此實現此接口的類都要實現契約裏邊的方法。
ServiceContainer (契約的具體實現) ,實現 Contracts。具體實現邏輯寫在這裏。

ServiceProvider (服務提供者) ,綁定事務到服務容器。

App (服務容器), 在服務提供者中,能夠經過 $this->app或App:: 獲得。 App::其實就是門面。

Facades(門面), 簡化服務提供者的調用方式,用靜態的方式調用具體實現裏的方法。

Ioc全稱Inversion of Control,意爲控制反轉

https://learnku.com/articles/789/laravel-learning-notes-the-magic-of-the-service-container

  

 

 

二、代碼簡單理解laravel

  2.1sql

         

Interface Power
{
    public function fight();
}

class FlyPower implements Power
{
    public function fight()
    {
        echo "我是飛行能力" . PHP_EOL;
    }
}

class FirePower implements Power
{
    public function fight()
    {
        echo "我是開火能力" . PHP_EOL;
    }
}

class XrayPower implements Power
{
    public function fight()
    {
        echo "我是x光線能力" . PHP_EOL;
    }
}



class Superman
{
    private $power;

    public function __construct(Power $power)
    {
        $this->power = $power;
    }

    public function go()
    {
        $this->power->fight();
    }
}

$power1 = new FlyPower();
$power2 = new FirePower();
$power3 = new XrayPower();
$superman1 = new Superman($power1);
$superman1->go();
$superman2 = new Superman($power2);
$superman2->go();
$superman3 = new Superman($power3);
$superman3->go();

   在這個例子中,SuperMan的構造方法須要一個參數,該參數必須實現Power接口,而這個參數是咱們在類的外部進行實例化後傳入的(看成參數),只要是Power的實現就都有效,因此至關於 把Power注入到Superman中,即依賴注入數據庫

    2.2 容器:依據簡單的理解,作的簡單的容器,這個容器 須要本身綁定,本身注入,而lavarel 實現的的反向依賴注入,自動注入編程

      

class Container
{
    protected $binds;

    protected $instances;

    public function bind($abstract, $concrete)
    {
        if ($concrete instanceof Closure) {
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;
        }
    }

    public function make($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        array_unshift($parameters, $this);

        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}


// 創建容器
$container = new Container();
// bind方法:第一個參數能夠理解爲要綁定的key,別名,第二個參數能夠是一個回調函數,也能夠是一個實例對象
$container->bind('flypower', function ($container) {
    return new FlyPower();
});
// bind的第二個參數爲回調函數的情況
$container->bind('firepower', new FirePower());
// bind的第二個參數爲實例對象的情況
$container->bind('superman', function ($container, $power) {
    return new Superman($container->make($power));
});

$superman1 = $container->make('superman', ['flypower']);
$superman2 = $container->make('superman', ['firepower']);
$superman1->go();// 輸出:我是飛行能力
$superman2->go();// 輸出:我是開火能力

      2.3 模擬lavarel 是實現的自動 反向依賴注入:api

   

  • php反射用法閉包

  • 閉包的use用法app

         

class Container
{
protected $bindings = [];

public function bind($abstract, $concrete = null, $shared = false)
{
if (!$concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = ['concrete' => $concrete, 'shared' => $shared];
}


protected function getClosure($abstract, $concrete)
{
/*
* use 的使用
*/
return function ($c) use ($abstract, $concrete) {
$method = ($abstract == $concrete) ? "build" : "make";
return $c->$method($concrete);
};
}

public function make($abstract)
{
//這裏的註釋在調試時,建議打開,能夠直觀的跟蹤程序的執行順序和流程
//static $i = 1;
//echo "make-".$i++.PHP_EOL;

$concrete = $this->getConcrete($abstract);

if ($this->isBuildable($concrete, $abstract)) {

$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
return $object;
}

protected function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}


protected function getConcrete($abstract)
{
if (!isset($this->bindings[$abstract])) {
return $abstract;
}
return $this->bindings[$abstract]['concrete'];
}


public function build($concrete)
{
//這裏的註釋在調試時,建議打開,能夠直觀的跟蹤程序的執行順序和流程
//static $i = 1;
//echo "build-".$i++.PHP_EOL;

if ($concrete instanceof Closure) {
return $concrete($this);
}
/*
* 反向依賴
*/
$ref = new ReflectionClass($concrete);

if (!$ref->isInstantiable()) {
echo "The $concrete is not instantiable.";
exit;
}
$constructor = $ref->getConstructor();
if (is_null($constructor)) {
return new $concrete;
}
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);
return $ref->newInstanceArgs($instances);
}


protected function getDependencies($parameters)
{
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (is_null($dependency)) {
$dependencies[] = null;
} else {
$dependencies[] = $this->resolveClass($parameter);
}
}

return (array)$dependencies;

}

protected function resolveClass(ReflectionParameter $parameter)
{
return $this->make($parameter->getClass()->name);
}
}

$c = new Container();
$c->bind("Power", "FlyPower");
$c->bind("superman1", "Superman");
$superman1 = $c->make("superman1");
//$c->bind("Superman","Superman");
//$superman1 = $c->make("Superman");
$superman1->go(); //輸出:我是飛行能力

  

三、代碼擴展yii

   3.1 多個db接口的擴展

    

//數據庫接口
interface Sql
{
    public function query();
}

class Mysql implements Sql {

      public function __construct(){}

      public function query()
      {
          echo "Mysql is working!\n";
      }
}

class Postgresql  implements Sql  {

      public function __construct(){}

      public function query()
      {
          echo "Postgresql is working!\n";
      }
}

class MSsql{

    public function query()
      {
          echo "MSsql is working!\n";
      }
}

class doQuery{
    protected $dosql;

    public function __construct(Sql $sql, A $a)
    {
        $this->dosql = $sql;
        $this->a = $a;
    }

    public function query()
    {

        $this->a->do();
        $this->dosql->query();
    }
}

class A{
    public function do()
    {
        echo "A works!\n";
    }
}

$app = new Container();
// Sql的實現,也就是concrete爲Postgresql
$app->bind("Sql", "Postgresql");
// myQuery是abstract,能夠看成別名,而doQuery是其實現
$app->bind("myQuery", "doQuery");
$app->bind("closure", function($c){
    echo "closure works!\n";
});
echo "\n\n";
$app->make("closure");
echo "\n\n";
$app->make("A")->do();
echo "\n\n";
// make的過程:
// 1. 因爲建立的是myQuery,因此找到doQuery
// 2. 容器在初始化doQuery時發現他是閉包,因而執行$app->make("doQuery")
// 3. 回到make的getConcrete(),發現返回doQuery,由於沒有doQuery對應的bind
// 4. isBuildable()發現$concrete === $abstract,因而可build
// 5. 進入build流程,跳過閉包檢測,開始執行反射
// 6. 若是沒有構造函數,直接實例化,若是有,解析出依賴
// 7. 拿出一個依賴,若是不爲空,進入依賴解析環節,此時發現doQuery依賴$db
// 8. $db實現了Sql的接口,因而調用$app->make(Sql),而SQL在初始化的時候被bind到了Postgresql上
// 9. 回到3
$myQuery = $app->make("myQuery");
echo "\n\n";
$myQuery->query();
echo "\n\n";
$app->bind("Sql", "Mysql");
$myQuery = $app->make("myQuery"); 
$myQuery->query();
echo "\n\n";
// MSsql沒有依賴,getConcrete返回MSsql,而後實例化
$myQuery = $app->make("MSsql");
$myQuery->query();

 

 

四、lavarel 部分代碼解析

    

簡單的綁定

$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });

單例模式綁定

 
 

經過 singleton 方法綁定到服務容器的類或接口,只會被解析一次。

 



也能夠經過  方法把具體的實例綁定到服務容器中。以後,就會一直返回這個綁定的實例:$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });instance
$api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\API', $api);
 

 

 

 

 

 

 

 

相關連接:

 

Laravel 學習筆記 —— 神奇的服務容器

php di

http://php-di.org/doc/getting-started.html

 

Laravel - 核心概念 - 服務容器(IoC) - 綁定

https://www.yy-tao.com/detail/208.html

 

https://www.cntofu.com/book/107/Laravel%20Container%E2%80%94%E2%80%94IoC%20%E6%9C%8D%E5%8A%A1%E5%AE%B9%E5%99%A8.md

相關文章
相關標籤/搜索