服务提供者-注

服务提供者-注

作者:韩忠康
原文:https://learnku.com/docs/laravel/5.5/providers
中文(laravel-china翻译):https://learnku.com/docs/laravel/5.5/provi...
Laravel版本:5.5
本文是Laravel文档中服务提供者章节的解析,建议与文档对照阅读。

[TOC]

概述

官方文档中关于服务提供者的这篇文档,主要是说明如何编写自定义的服务提供者。本篇注解,就来说说啥是服务提供者,然后再总结一下如何编写服务提供者。

服务提供者想要理解,首先需要理解什么是服务容器,可以移步 服务容器-注 进行了解。在有了服务容器的概念后,就可以很容易的理解什么是服务提供者了。

服务容器是盛放服务的容器,有绑定和解析两个主要操作。绑定就是将服务注册到容器中。而服务提供者,就是完成绑定服务到容器任务的单元。看名字就知道,是用来提供服务的单位。简言之,服务容器中绑定的服务,就是由服务提供者绑定进去的。就是这么个功能。 详细看看。

Laravel中服务提供者的结构

服务提供者类,都要继承自 Illuminate\Support\ServiceProvider 服务提供者基类。

服务提供者一定要有一个 register 方法,这个方法功能就是完成服务绑定到容器的。参考日志服务提供者 Illuminate\Log\LogServiceProvider register的实现:

class LogServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        // 绑定一个单例对象,用于提供日志服务
        $this->app->singleton('log', function () {
            return $this->createLogger();
        });
    }
}

可以看到register()方法,确实是完成服务容器的绑定。这就是服务提供者的主要任务。
除了这个register()方法外,还有boot()也是比较常用的方法,后边会提到。

绑定的时机

再看就是这个register()方法是在何时被调用的,就是何时完成注册的。

一部分,就是在应用服务容器初始化时,完成基础的服务绑定,参考代码:

文件 Illuminate\Foundation\Application :
构造方法:

public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }

        $this->registerBaseBindings();
        // 注册基础服务提供者,其他代码忽略
        $this->registerBaseServiceProviders();

        $this->registerCoreContainerAliases();
    }

调用的 Illuminate\Foundation\Application::registerBaseServiceProviders():

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}

这个 $this->register() 就可以触发对应的服务提供者的register()方法。

另一部分是在内核Kernel处理请求前,会绑定一些基础功能服务提供者。

可以参考代码 Illuminate\Foundation\Application::registerConfiguredProviders() :

public function registerConfiguredProviders()
{
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                ->load($this->config['app.providers']);
}

注意观察 $this->config['app.providers'] 这行代码就是从配置文件中读取配置好的服务提供者列表,进行注册绑定,app.providers' 这个配置可以在config/app.php` 中看到, 列举一部分:

'providers' => [
    /*
     * Laravel Framework Service Providers...
     */
    Illuminate\Auth\AuthServiceProvider::class,
    ... 好多,好多

    /*
     * Package Service Providers...
     */
    Laravel\Tinker\TinkerServiceProvider::class,

    /*
     * Application Service Providers...
     */
    ... 好多,好多
    App\Providers\RouteServiceProvider::class,
],

而这个 registerConfiguredProviders() 方法,是在 $kernel->handle()的内部进行调用的。详细的调用过程,大家可以利用ide进行追踪下。需要注意的就是,这个方法是在内核处理请求阶段执行的。

以上就是两处服务提供者,绑定服务的时机。1,应用服务容器初始化过程;2,内核处理请求过程中。

服务容器的boot()方法

一个基本的服务器容器,除了register()方法外,还会存在一个boot方法。对比register()方法,他们的主要差异是执行时机不同,进而导致了所完成的任务特征也有所不同。
register(), 是服务提供者的主要任务,就完成某些服务的注册。使用服务提供者,核心目的就是为了注册服务。
boot(),是为了初始化某些服务特殊工作。任何服务提供者的boot()方法都会在全部服务提供者注册之后运行。那也就可以保证在boot()方法中,可以使用全部的注册服务。而对应的register()是在每个服务提供者注册时执行,其中不能使用其他服务提供者绑定的服务,因为我们不能完全确定其先后顺序。除非你非常熟悉。

执行时机的差异,可以参考代码:Illuminate\Foundation\Http\Kernel::$bootstrappers 属性,即时在内核处理请求时,需要启动的功能数组定义:

protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    // 先启动注册服务提供者,触发服务提供者的register()方法
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    // 注册完毕后,在启动boot服务提供者,触发全部服务提供者的boot()方法
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
];

注意此数组的最后两个元素,显示注册服务提供者,再时启动服务提供者。这就确定了服务提供者方法的执行时机。

延迟绑定的优化

app.providers 配置项中,这么多的服务提供者,是不是都需要在初始化公共阶段完成绑定呢?如果某些服务器在整个周期可能会用不到呢,那岂不是白白绑定了?Laravel通过 将某些服务提供者设置为延迟状态,来解决这个性能问题。

就是说,如果某个服务提供者被设置为延迟处理,那么不会在初始化时就绑定,而是在真正使用时才绑定,使用属性 $defer = true 就可做到。那么启动阶段在处理到该服务提供者时,如果发现是延迟的,那么不立即绑定,等到需要时在绑定。

参考某个访问(Laravel文档提供):

class RiakServiceProvider extends ServiceProvider
{
    /**
     * 是否延时加载提供器。
     *
     * @var bool
     */
    protected $defer = true;
}

定义超级简单,一个属性搞定。这是个内部优化措施。与整体结构关系不大!

Laravel文档概要

注解完毕后,最官网文档做个概要:
主要说如何编写服务提供者,过程如下:

  • 通过 artisan make:provider 来创建基础服务提供者结构
  • 实现注册 register() 方法
  • 实现注册 boot() 方法,没有可以留空
  • 在配置文件中,配置定义好的服务提供者

经过以上步骤,就完成了服务提供者的定义。当我们需要在Laravel中增加自己的服务时,就可以使用服务提供者来绑定我们的服务到服务容器,便于后期依赖注入到需要的位置!

结语

总结一下 服务,服务容器,服务提供者的关系,看个图总结一下:

服务-容器-提供者的关系

就这么多!

Laravel-helloKang

更新内容,请关注微信公众号,小韩说理:
小韩说理

本作品采用《CC 协议》,转载必须注明作者和本文链接
韩忠康 坚持锻炼,坚持学习
本帖由 Summer 于 6年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 2

更一步理解laravel运行原理

6年前 评论

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