laravel依赖注入、Facades的实例、工厂模式

0.354字数 976阅读 2538

Facades 工作原理#

  • 任何一个类里面,只要有构造函数依赖的,都可以注入

  • 服务提供者里面,registry方法是注册容器,boot方法是启动的时候进行的初始化工作,还有map方法,把服务提供者做一个映射

第一段

下面我做一个测试实现依赖注入

首先在app下面自己写一个类, vi /app/U.php

//  app/U.php
<?php
namespace App;
class U
{
    public function show()
    {
        return 'this is U show';
    }
}

然后我们在控制器里面的构造函数里实现注入。这样,整个控制器都可以使用U.php类里面的所有函数了

<?php
namespace App\Http\Controllers;
class TestController extends Controller
{
    protected $u;
    public function __construct(\App\U $a)
    {
        $this->u = $a;
    }
    public function test()
    {
        return $this->u->show();
    }
}
依赖注入打印结果
  • 我们也可以直接绑定(bind),然后直接生产(make)
Route::get('/test',function (){
    App::bind('U',function(){  
 // 这里使用$this->app->bind()应该也可以,但是我这里测试不成功,就换成了App这个静态方法了
        return new \App\U();    //   我们这里实例化那个类,就是绑定的哪个类
    });
    $res = App::make('U');
    return $res->show();
});

第二段

  • 但是我们如果能够直接用别名的方式来替代\App\U就更好了。那么我们就需要起一个别名。为了方便区分理解,我们此处把别名叫做UA

第一步:把U这个类,绑定(注册)至容器。

我们在app/Providers/AppServiceProvider.php里面注册U这个类,起个名字为UA.

public function register()
    {
        $this->app->bind('UA',function($app){
            return new \App\U();
        });
    }

第二步,使用Facades给绑定的UA做成静态方法

首先我们还是可以在app下面自己写一个类, vi /app/UF.php

<?php
//   app/UF.php
namespace App;
use Illuminate\Support\Facades\Facade;
class UF extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'UA';  //  此处返回的是服务容器中绑定的名字,我们刚才把`U.php`类绑定成了`UA`
    }
}

这个时候我们就可以使用静态方法UF就能替代U.php这个类了。不需要再在构造函数中注入了

<?php
namespace App\Http\Controllers;
class TestController extends Controller
{
   public function test()
    {
        return \App\UF::show();
    }
}
//  打印出来的结果,和依赖注入打印结果是一样的
image.png
image.png

第三段

  • 我们也可以给UF.php这个类起别名F
    很简单,只需要在第二段的基础上做修改。
    app/config/app.php里面的aliases数组里面添加'F' => \App\UF::class,即可。
把`UF.php类`注册别名
  • 这里我是为了方便大家区分,故意把注册的U.php类写成了,UA,UF,F。其实三个名字都应该写成U,这样才方便我们辨别。框架里面的都是统一的。比如下图的View类别名
图1.png

图2.png

还有一个问题就是,如果我们需要看框架里面那些类怎么办的。我们每次打开只能打开到图1的那种情况,我们可以看手册Facades章节的Facade 类参考#


一些听课笔记也在这里写下

  • 任何一个类里面,只要有构造函数依赖的,都可以注入
laravel中所谓的容器其实就是一个工厂,可以帮助我们创建对象。使用容器可以方便的实现创建某个接口的多个实现类。
容器剋在任何可以访问到app对象的地方绑定 (注册),但通常我们会在服务提供者的register方法中进行多个容器的绑定。
laravel框架默认为我们保留了一个服务提供者,AppServiceProvider.php 其中register是空白的,我们可以将自己的容器配置到里面。

Contracts 的好处是,我们的接口是一定的,但是我们的服务可以随便修改。这样做到了低耦合。

有了服务容器,我们才可以make 出来东西
没有在服务提供者里面注入的话,也make 不出来东西

服务提供者里面,register方法是注册容器,容器是帮我们创建对象的。不过,服务提供者本身,也需要被注册,就是在`app/config/app.php`里的`providers`数组里面
boot方法是启动的时候进行的初始化工作,还有map方法,把服务提供者做一个映射

laravel工厂模式

  • 其实服务提供者,就是框架给我们提供了一个可配置的工厂
  • 服务提供者是在框架里面为我们准备了一个类似于工厂的工具
  • 服务容器,就是具体的帮助我们实现依赖注入,控制反转的工具

所谓的工厂模式,就是我们需要什么类,我们就绑定什么类。比如刚才我们绑定了 U类,现在我们需要一个U2类,我们就直接绑定U2类.
但是此处的U2类,应该是与U类有一样的方法,一样的接口。这样,我们直接用F::方法中的方法都不需要改变。适用场景,比如原来用了mysql,现在改成orcal

public function register()
    {
        $this->app->bind('UA',function($app){
            return new \App\U();
        });
    }

laravel的服务提供者provider

简单讲解

其实服务提供者本质就是
1.注册一个类
2.生成一个继承ServiceProvider的 Provider 
在register 函数里面实现app的绑定这个类
3.在config/app.php的providers数组里面添加第二步生成的provider类
4.在使用的时候直接app('')或者app()->make('')即可
  • 实例演示
1.生成Billing类
<?php
namespace App;
class Billing{
    public function test()
    {
        return 'test';
    }
}

2.
php artisan make:provider  BillingProvider
修改里面的register函数
public function register()
    {
        $this->app->bind('billing',function (){
            return new \App\Billing();
        });
    }

3. config/zpp.php里面
'providers' => [
     .....
        \App\Providers\BillingProvider::class,

    ],
4 使用
public function index()
    {
        $res = app('billing');
// 或者   $res = app()->make('billing');
        dd($res->test());
    }
  
  • 如果想要实现facede也很简单,我们基于以上步骤来做
5.生成一个继承Facade的Bill类
<?php
namespace App;
use Illuminate\Support\Facades\Facade;

class Bill extends Facade {
    protected static function getFacadeAccessor()
    {
        return 'billing';  
    }
}

// 函数的return 必须是 billing  代表的是 Billing这个类
其实相当于是 app()->bind('billing')
Facade这个类中有public static function __callStatic($method, $args) 函数
如果我们使用的方法不存在,就会执行这个静态方法

6.config/app.php里面

'aliases' => [
'Bill' => \App\Bill::class,
]
这个时候,我们就可以直接
 public function index()
    {
        $res = Bill::test();
        dd($res);
    }
但是这种方式比provider多执行了一些东西,其实效率是偏慢的

服务容器
服务容器与容器与工厂

application是一个工厂
首先他要绑定一个类或者对象,然后才可以实例化使用

首先绑定一个类或者对象,同时给他起一个名字 
$this->app->bind('\A', function ($app) {    
  //1.使用绑定方法,回调函数都要return一个类型的对象   
        return new App\User();    
    });
//然后当你需要这个对象的时候,你就make生产   
 $u = $this->app->make('A');
 //$this->app->make('A') 等价于 $this->app['A'];


 singleton()绑定默认只会创建一个对象
$this->app->bind('\A', function () {       
        return new App\User();    
    });
$u = $this->app['A'];
 $u->title = 'abc';
 $u2 = $this->app['A'];
 dd($u2);  //打印出'abc
如果是singleton()只能创建一个对象

使用绑定方法,回调函数都要return一个类型的对象
你也可以使用 instance方法绑定一个已经存在的对象至容器中。后面的调用都会从容器中返回指定的实例:

     $this->app->bind('\A', function () {    
        return new App\User();    
    });
   这句话还可以写成  $this->app->bind('A',App\User::class);

    $api = new HelpSpot\API(new HttpClient);
    $this->app->instance('HelpSpot\Api', $api);

我们来看一个app/bootstrap/app.php中的一个绑定

    $app->singleton(    
     //这个只是一个接口,这个接口有很多的实现 
          Illuminate\Contracts\Http\Kernel::class,   
  //这个只是具体的实现这个接口的一个类
          App\Http\Kernel::class
    );


    绑定接口至实现
    $this->app->bind( 'App\Contracts\EventPusher',    //接口
                      'App\Services\RedisEventPusher'  //实现
                    );

依赖注入

一个工厂类来帮我们创建我们的对象的同时,把我们这个对象创建的时候需要的参数也一起注入进来
这个过程就叫做控制反转。把创建对象的责任交给容器,交给工厂,而不是我们自己

1.我们先创建两个类,比如在app目录下TA.php TB.php两个文件夹下两个类
namespace   App;
class TA
{
    private $title;
}

namespace   App;
class TB
{
    private $ta;
    public function __construct(\App\TA $a){
        $this->ta = $ta;
    }
}
   2.在app/bootstrap/app.php中写好一个绑定
  绑定的时候,如果命名空间从根目录下引用"\",则此命名空间加不加引号都行
   $app->bind( 'App\TA::class', '\App\TA::class');  //前面这个必须加引号,后面这个可写可不写
    $app->bind( '\App\TB::class', \App\TB::class);
    3.然后我们在路由页面随便做一个测试
    Route::get('/test',function(){
         $tb = $this->app->make( '\APP\TB::class' ); //引号可写可不写
         dd($tb);
     });

实现添加自己组建的流程

1.先在config/app.php里面的  providers 数组里面添加自己的 provider类
App\Library\Tool\ToolServiceProvider::class,
自己的provider类写在哪里都无所谓,只要跟数组中对上即可
2.在provider类里面  register 写入自己$this->app->singleton绑定的类,这个类是自己真正需要的功能类
singleton中第一个参数是随便的一个名称,第二个参数才是类名
3.创建一个facade类
protected static function getFacadeAccessor()
    {
        return \App\Library\Tool\source\Common::class;
    }
}
这个return中的值,其实是刚才singleton 绑定的第一个参数
4.在config/app.php中的aliases数组里面添加别名
 'Common' => App\Library\Tool\CommonFacade::class, 
这个值对应的是3中的类名

推荐阅读更多精彩内容