zoukankan      html  css  js  c++  java
  • laravel5.8 IoC 容器

    网上 对容器的解释有很多,这里只是记录,搬运!

    1、简单理解:

      

    2019-10-10 11:24:09
    解析 lavarel 容器
    利用 IoC 将你设计好的对象交给容器控制,而非传统地在你的对象内直接控制、处理。
    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 类实际上是 IlluminateSupportFacadesRoute 通过 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
    

      

    2、代码简单理解

      2.1

             

    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 是实现的自动 反向依赖注入:

       

    • php反射用法

    • 闭包的use用法

             

    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(); //輸出:我是飛行能力

      

    3、代码扩展

       3.1 多个db接口的扩展

        

    //数据库接口
    interface Sql
    {
        public function query();
    }
    
    class Mysql implements Sql {
    
          public function __construct(){}
    
          public function query()
          {
              echo "Mysql is working!
    ";
          }
    }
    
    class Postgresql  implements Sql  {
    
          public function __construct(){}
    
          public function query()
          {
              echo "Postgresql is working!
    ";
          }
    }
    
    class MSsql{
    
        public function query()
          {
              echo "MSsql is working!
    ";
          }
    }
    
    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!
    ";
        }
    }
    
    $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!
    ";
    });
    echo "
    
    ";
    $app->make("closure");
    echo "
    
    ";
    $app->make("A")->do();
    echo "
    
    ";
    // 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 "
    
    ";
    $myQuery->query();
    echo "
    
    ";
    $app->bind("Sql", "Mysql");
    $myQuery = $app->make("myQuery"); 
    $myQuery->query();
    echo "
    
    ";
    // MSsql没有依赖,getConcrete返回MSsql,然后实例化
    $myQuery = $app->make("MSsql");
    $myQuery->query();

    4、lavarel 部分代码解析

        

    简单的绑定

    $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);
     

    相关链接:

    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

  • 相关阅读:
    第七周学习进度
    环形数组求子数组最大和
    第六周学习进度
    团队
    子数组和最大值
    第五周学习进度
    第四周学习进度
    构建之法第一篇阅读笔记
    关于Sublime text 的PHP编译环境配置的问题
    PHP基础之 数组(二)
  • 原文地址:https://www.cnblogs.com/cbugs/p/11654690.html
Copyright © 2011-2022 走看看