服务容器/服务/服务提供者

服务容器

你了解了这个之后就知道这个玩意有多强大,这里推荐一篇蛮好的文章:Ioc模式介绍,去看的时候提一点小建议:

  • Ioc模式是如何解决依赖自动注入的
  • Ioc模式比起一般的手工添加有什么优点,他又存在哪些缺点

具体是如何实现的,那么可以直接看那位大哥的代码或者我下面抄来的例子。

基础代码

假设情景:
一个Traveller去西藏旅行(visitTibet),他可以选择朝圣(leg,用脚徒步走过去),或者自驾游(car),或者搭乘火车(train)。

下面开始编写上面情景的代码:

基础类

<?php
interface Visit{
    public function go();
}
class Leg implememts Visit{
    public function go(){
        echo "朝圣";
    }
}
class Car implements Visit{
    public function go(){
        echo "自驾游";
    }
}
class Train implements Visit{
    public function go(){
        echo "乘火车";
    }
}

上面定义了几个基础类,下面定义旅行者类:

<?php
class Traveller{
    protected $trafficTool;
    public function __construct(Visit $traficTool){
        $this->trafficTool=$traficTool;
    }
    public function visitTibet(){
        $this->trafficTool->go();
    }
}

这里不再赘述如果需要实例化多个Traveller时需要多麻烦之类的了,这里直接介绍Ioc模式

Ioc容器类

在开始看下面的代码前,先看一下其作用结果,这样可以减少你的疑惑:

$app=new Container();
$app->bind('Visit','Train');
$app->bind("traveller","Traveller");
$traveller=$app->make('traveller');
$traveller->visitTibet();

其中只要调用bind就能实现依赖类的自动关联,之后只要使用make函数就能实现类的自动实例化,并且依赖被自动处理了。

class Container{
    protected $bindings=[];
    public function bind($abstract,$concrete=null,$shared=false){
        if(!$concrete instanceof Closure){
            $concrete=$this->getClosure($abstract,$concrete);
        }
        $this->bindings[$abstract]=compact('concrete','shared');
    }
    public function getClosure($abstract,$concrete){
        return function($c) use ($abstract,$concrete){
          $method=($abstract==$concrete) ? "build":"make";
          return $c->$method($concrete);
        };
    }
    public function make($abstract){
        $concrete=$this->getConcrete($abstract);
        if($this->isBuildable($concrete,$abstract)){
            $object=$this->build($concrete);
        }else{
            $object=$this->make($concrete);
        }
        return $object;
    }
    public function getConcrete($abstract){
        if(!isset($this->bindings[$abstract])){
            return $abstract;
        }
        return $this->bindings[$abstract]['concrete'];
    }
    public function isBuildable($concrete,$abstract){
        return $concrete===$abstract || $concrete instanceof Closure;
    }
    public function build($concrete){
        if($concrete instanceof Closure){
            return $concrete($this);
        }
        $reflector=new ReflectionClass($concrete);
        if(!$reflector->isInstantiable()){
            echo "<span style='color: red;font-size: large;font-weight: bolder'>你个傻屌,这个类无法被实例化!!!</span>";
        }
        $constructor=$reflector->getConstructor();
        if(is_null($constructor)){
            return new $constructor;
        }
        $dependencies=$constructor->getParameters();
        $instances=$this->getDependencies($dependencies);
        return $reflector->newInstanceArgs($instances);
    }
    public function getDependencies($parameters){
        $dependencies=[];
        foreach ($parameters as $parameter){
            $dependency=$parameter->getClass();
            if(is_null($dependency)){
                $dependencies[]=null;
            }else{
                $dependencies[]=$this->resolveClass($dependency);
            }
        }
        return (array) $dependencies;
    }
    public function resolveClass(ReflectionParameter $parameter){
        return $this->make($parameter);
    }
}

服务

所谓服务其实就是上面绑定到bind中的类,至于这些类有什么作用,暂时还没有遇到,这里可以暂时举一个简单的例子,就在public/index.php文件中添加下面的内容:

容器类:App调用方式

这里先列举几种调用app类的方法:

//方式1
$app = require_once __DIR__.'/../bootstrap/app.php';
//方式2
$app=$this->app;
//方式3
app();
//方式4:
\App::

绑定服务方式

//待绑定的类
class GeneralService {}
class GeneralService2{}
class GeneralService3{}
class GeneralService4{}
interface GeneralService5 { }
class GeneralService6 implements GeneralService5{}
//绑定服务方式1
$app->bind(GeneralService::class,function($app){
    return new GeneralService();
});
//绑定服务方式2
$app->singleton(GeneralService2::class,function ($app){
    return new GeneralService2();
});
//绑定服务方式3
$instance=new GeneralService3();
$app->instance('GeneralService3',$instance);
//绑定服务方式4
$app->bind(GeneralService4::class,GeneralService4::class);
//绑定服务方式5
$app->bind(GeneralService5::class,GeneralService6::class);

服务提供者

命令行生成服务提供者

创建服务提供者:php artisan make:provider ProviderName

服务提供者基础模板

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ProviderName extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     * 这个函数在register之后运行
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     * 该函数会在该服务提供者被注册时执行,该服务提供者何时运行看情况,注意,该函数不能向其中传递任何参数,因为调用时是没有传递参数的,
     * 所以即使要传递参数也要设置默认值的形式
     * @return void
     */
    public function register()
    {
        //
    }
}

关于为什么先调用register,之后再调用boot,可以看这篇文章:Laravel服务容器启动过程

所以在Laravel启动后已经默认启动了一些服务提供者,我们定义在/config/app.php中的providers配置项,也会在之后被加载,所以如果我们要自定义服务提供者,那么只需要将新建的服务提供者添加到该配置文件中。

设置服务提供者启动方式

如果上面的ProviderName服务提供者代码直接在config/app.php中直接注册的话,那么随着系统启动,该服务提供者也会自动调用,但是当我们想要这样设置时:当某一具体服务被调用时,该服务提供者被调用,则可以修改服务提供者:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ProviderName extends ServiceProvider
{
    //第一步,将 $defer 属性设置为 true
    protected $defer=true;
    public function boot()
    {
        //
    }

    public function register()
    {
        //第二步 不一定需要,但是当你下面需要额外的依赖时,就可以在这里绑定进服务容器中
        //$this->app->bind(NameSpace/ClassName::class)
    }
    // 第三步 设置何时调用该服务提供者,即注册何种服务时实例化该服务提供者
    public function provides(){
        return [ClassName1::class,ClassName2::class];
    }
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐