Laravel 服务容器和提供器

看到laravel手册的服务容器和提供器模块,概念有些模糊,网上搜索+动手实践了一番,记录理解。

laravel框架默认加载了很多的服务,一个服务就是一个类,比如Auth,Cache,Route等等。如果想自定义一个服务怎么办,很简单,自己创建一个类然后再绑定就行。服务容器和服务提供器是没有直接关系的,一般都是按照什么提供器就提供什么服务的规则来命名两者,但是你创建一个 火车serviceprovider, 然后再这个火车serviceprovider里绑定汽车类,那么火车serviceprovider提供的就是汽车对象,虽然用起来很尴尬,但你自己定义的你自己肯定会用的,别人估计是哭着用完的。
自定义的功能类所具有的功能如果需要规范点,那就先写个接口,比如Cache类,需要拥有get,set方法等,写个接口加以规范,以后用的时候也只需要调用实现了这个接口的类就行,不写接口也无所谓。

1、创建接口类,接口类的名字随便起,但是最好还是规范点,自己看着也舒服
创建 app\Contract\SktContract.php 接口类

namespace App\Contract;

interface SktContract
{
    //展示队伍队员名单
    public function list_team($team_name);
}

2、接下来按照这个接口来实现你的功能类
创建 app\Service\SktService.php

namespace App\Service;
use App\Contract\SktContract;
class SktService implements SktContract
{
    public $name = null;
    //展示队伍队员名单
    public function list_team($team_name)
    {
        echo $team_name."的队员名单是Faker-bengi";
    }
}

3、既然功能类写好了,那就要把他装在服务提供器中,到时候别人拿着你的服务提供器,就能获得实例化后的功能类对象了。
创建app\Providers\SktServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Service\SktService;
class SktServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        //使用singleton绑定单例
        $this->app->singleton('skt',function(){
            return new SktService();
        });

        //使用bind绑定接口
        $this->app->bind('App\Contracts\SktContract',function(){
            return new SktService();
        });
    }
}

这了绑定了两次,一次用singleton绑定的,一次用bind方法绑定的,区别是singleton绑定的是单例,继续往下看就知道区别了。

4、既然服务提供器也准备好了,那就把提供器加到框架的加载列表中,之后就可以在控制器中使用啦。
config/app.php在providers键名对应的数组中的最后,加入这行

App\Providers\SktServiceProvider::class,

5、接下来我们在控制器中使用我们刚刚自定义的服务

    public function index()
    {
        $fooService1 = app()->make('App\Contracts\SktContract'); //这里产生的不是单例,这里用bind方法绑定的
        $fooService2 = app()->make('App\Contracts\SktContract');

        $fooService3 = app()->make('skt'); //这里产生的是单例,666,因为这里skt是用singleton绑定的
        $fooService4 = app()->make('skt'); //这里产生的是单例,666

        $fooService1->name='111';
        $fooService2->name='222';
        $fooService3->name='333';
        $fooService4->name='444';

        $fooService1->list_team('LOL-ssb'); //LOL-ssb的队员名单是Faker-bengi
        var_dump($fooService1); //111
        var_dump($fooService2); //222
        var_dump($fooService3); //444
        var_dump($fooService4); //444
    }

通过app()->make('提供器中绑定时的键名');来获取功能类对象,用bing方法绑定的,获取出来的都是新对象,用singleton绑定的,获取的是单例。

6、那么问题来了,觉得这样调用还是很麻烦,能不能再简化调用?可以~ laravel有个facade功能。接下来我们创建一个文件:/vendor/laravel/framework/src/Illuminate/Support/Facades/Skt.php

namespace Illuminate\Support\Facades;

use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;

/**
 * @method static bool list_team() //加上这行注释是为了让php_storm编辑器能够追踪函数
 * Class Skt
 * @package Illuminate\Support\Facades
 */
class Skt extends Facade  //类名随便起,但是为了规范还是和功能类为主也叫Skt吧(功能类是叫SktService)
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor
    {
        return 'skt'; //这里一定要return一个绑定的键名,代表你接下来会获得那个键名绑定的对象
    }
}
上面这个类主要是getFacadeAccessor方法返回的字符串要和你绑定时候的键名对应上,这里返回的是skt,之前在服务提供器中,不是有一句
        $this->app->singleton('skt',function(){ //注意,这里的键名是skt
            return new SktService();
        });

吗,那么返回的就是这个return new SktService();对象。

7、好了,Facades文件也写完了,接下来我们回到控制器中,把调用功能类的代码直接改为以下代码

use Illuminate\Support\Facades\Skt; //这里要引入facades
class Test extends Controller
{
public function test()
    {
        Skt::list_team('LOL-ssb');
    }
}

这里引入了 Illuminate\Support\Facades\Skt;获得了一个叫做Skt的类,但是不好意思,这个类和你的功能类SktService只是名字有点像,但是完全没有关系,起作用的是这个Skt类的getFacadeAccessor方法return了一个'skt'字符串,然后框架内部获取到这个'skt‘字符串之后,又根据这个'skt'字符串为键名,找到了之前通过singleton方法绑定的对象,那个对象才是真正的功能类。Skt::list_team()调用的实际上是SktService类的list_team()方法

总结:
接口类SktContract、功能类SktService、服务提供器SktServiceProvider(将功能类在里面绑定好)、将服务提供器加入到框架加载列表config/app.php、创建facades文件Skt(关键是renturn一个绑定时的键名)

实际上想要偷懒的话大可不必这么麻烦,创建一个SktService.php功能类,然后再框架原有的AppServiceProvider里面的register方法进行绑定也行,而且AppServiceProvider默认就在框架的加载列表中了,直接用就行,不过facades文件还是要自己创建。这样做的好处是省事,但是一旦各种各样的功能都在AppServiceProvider类中进行绑定的话,就会很乱,不符合单一功能原则,最好还是一个功能类就创建一个服务提供器。不过要想引入功能类大可不必这么麻烦呢,直接用trait,哪里想用就use进来就行了,省事。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5

好文章,对laravel了解的更深入了

5年前 评论

@风中的白鸽 哈哈,谢谢捧场 :grin:

5年前 评论

不错,不错,非常好。

5年前 评论

简单明了,看了好多了DI IOC啥都是在讲原理 都没有讲怎么使用 挠头 看了瞬间明白了 这玩意是啥..

4年前 评论
Code_Er

你好我想请问一下 如果是门面的话是需要绑定单例才能使用吗? 还有在服务提供器里面的绑定跟单例的实际区别是?

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!