我们知道, Container 有很多种 「绑定」 的姿势,比如 bind() , extend() , singleton() , instance() 等等,那么 Laravel 中怎样「注册」这些「绑定」呢?那就是 Service Provider
先看下 Laravel 文档中这句话:

Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel’s core services are bootstrapped via service providers.

Service Providers (服务提供者)Laravel 「引导」过程的核心。这个「引导」过程可以理解成「电脑从按下开机按钮到完全进入桌面」这段时间系统干的事。

概览

Service Provider 有两个重要的方法:

  1. namespace App\Providers;
  2. use Illuminate\Support\ServiceProvider;
  3. class AppServiceProvider extends ServiceProvider
  4. {
  5. /**
  6. * 注册服务.
  7. *
  8. * @return void
  9. */
  10. public function register()
  11. {
  12. //
  13. }
  14. /**
  15. * 启动服务。
  16. *
  17. * @return void
  18. */
  19. public function boot()
  20. {
  21. //
  22. }
  23. }

Laravel 在「引导」过程中干了两件重要的事:

  1. 通过 Service Providerregister() 方法注册「绑定」
  2. 所有 Servier Providerregister() 都执行完之后,再通过它们 boot() 方法,干一些别的事。

    过程分析

    这个「先后顺序」可以在 Laravel 的启动过程中找到:
    1). 首先,生成核心 Container : $app (实例化过程中还注册了一大堆基本的「绑定])。
    public/index.php

    1. /*
    2. |--------------------------------------------------------------------------
    3. | Turn On The Lights
    4. |--------------------------------------------------------------------------
    5. |
    6. */
    7. $app = require_once __DIR__.'/../bootstrap/app.php';
    1. // bootstrap/app.php
    2. $app = new Illuminate\Foundation\Application(
    3. realpath(__DIR__.'/../')
    4. );

    2). 接下来注册 Http\Kernel , Console\Kernel , Debug\ExecptionHandler 三个「单例」绑定。
    bootstrap/app.php

    1. $app->singleton(
    2. Illuminate\Contracts\Http\Kernel::class,
    3. App\Http\Kernel::class
    4. );
    5. $app->singleton(
    6. Illuminate\Contracts\Console\Kernel::class,
    7. App\Console\Kernel::class
    8. );
    9. $app->singleton(
    10. Illuminate\Contracts\Debug\ExceptionHandler::class,
    11. App\Exceptions\Handler::class
    12. );

    3). 然后「启动」应用。
    public/index.php

    1. /*
    2. |--------------------------------------------------------------------------
    3. | Run The Application
    4. |--------------------------------------------------------------------------
    5. |
    6. */
    7. $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
    8. $response = $kernel->handle(
    9. $request = Illuminate\Http\Request::capture()
    10. );

    4). 由于以前的「绑定」,$kernel 获取的其实是 App\Http\Kernel 类的实例,App\Http\Kernel 类又继承了 Illuminate\Foundation\Http\Kernel 类。
    handle() 方法执行了 sendRequestThroughRouter() 方法:

    1. // Illuminate\Foundation\Http
    2. class Kernel implements KernelContract
    3. {
    4. public function handle($request)
    5. try {
    6. //...
    7. $response = $this->sendRequestThroughRouter($request);
    8. } catch (Exception $e) {
    9. //...
    10. }
    11. }
    12. }

    5). 这个 sendRequestThroughRouter() 方法执行了一系列 bootstrappers (引导器)

    1. // Illuminate\Foundation\Http
    2. class Kernel implements KernelContract
    3. {
    4. protected function sendRequestThroughRouter($request)
    5. {
    6. //...
    7. // 按顺序执行每个 bootstrapper
    8. $this->bootstrap();
    9. //...
    10. }
    11. }

    6). bootstrap() 方法中又调用了 Illuminate\Foundation\Application 类的 bootstrapWith() 方法:

    1. // Illuminate\Foundation\Http
    2. class Kernel implements KernelContract
    3. {
    4. public function bootstrap()
    5. {
    6. if (! $this->app->hasBeenBootstrapped()) {
    7. //
    8. $this->app->bootstrapWith($this->bootstrappers());
    9. }
    10. }
    11. }
    1. // Illuminate\Foundation\Http\Kernel
    2. class Kernel implements KernelContract
    3. {
    4. protected $bootstrappers = [
    5. //...
    6. \Illuminate\Foundation\Bootstrap\RegisterProviders::class, // 注册 Providers
    7. \Illuminate\Foundation\Bootstrap\BootProviders::class, // 引导 Providers
    8. ];
    9. protected function bootstrappers()
    10. {
    11. return $this->bootstrappers;
    12. }
    13. }

    这里就能看出来 Service Providerregisterboot 的「先后顺序了」。

    后续的执行过程暂时先不介绍了。

这就意味着,在 Service Provider boot 之前,已经把注册好了所有服务的「绑定」。因此, 在 boot() 方法中可以使用任何已注册的服务。
例如:

  1. {
  2. namespace App\Providers;
  3. use Illuminate\Support\ServiceProvider;
  4. class ComposerServiceProvider extends ServiceProvider
  5. {
  6. public function boot()
  7. {
  8. // 这里使用 make() 方法,可以更直观
  9. $this->app->make('view')->composer('view', function () {
  10. //
  11. });
  12. // 或者使用 view() 辅助函数
  13. view()->composer('view', function () {
  14. //
  15. });
  16. }
  17. }

如果了解 Container 「绑定」 和 make 的概念,应该很容易理解上面的过程。
剩余的无非就是:

创建 Service Provier

  1. php artisan make:provider MyServiceProvider

注册绑定

  1. namespace App\Providers;
  2. use Illuminate\Support\ServiceProvider;
  3. class MyServiceProvider extends ServiceProvider
  4. {
  5. public function register()
  6. {
  7. $this->app->bind(MyInterface::class, MyClass::class);
  8. }
  9. }

引导方法

  1. namespace App\Providers;
  2. use Illuminate\Support\ServiceProvider;
  3. use Illuminate\Contracts\Routing\ResponseFactory;
  4. class MyServiceProvider extends ServiceProvider
  5. {
  6. /**
  7. * boot() 方法中可以使用依赖注入。
  8. * 这是因为在 Illuminate\Foundation\Application 类中,
  9. * 通过 bootProvider() 方法中的 $this->call([$provider, 'boot'])
  10. * 来执行 Service Provider 的 boot() 方法
  11. * Container 的 call() 方法作用,可以参考上一篇文章
  12. */
  13. public function boot(ResponseFactory $response)
  14. {
  15. $response->macro('caps', function ($value) {
  16. //
  17. });
  18. }
  19. }

config/app.php 中注册 Service Provider

  1. 'providers' => [
  2. /*
  3. * Laravel Framework Service Providers...
  4. */
  5. /*
  6. * Package Service Providers...
  7. */
  8. /*
  9. * Application Service Providers...
  10. */
  11. App\Providers\MyServiceProvider::class,
  12. ],

注意:Laravel 5.5 之后有 package discovery 功能的 package 不需要在 config/app.php 中注册。

延时加载

按需加载,只有当 Container 「make」 ServiceProviderproviders()方法中返回的值时,才会加载此 ServiceProvider
例如:

  1. namespace Illuminate\Hashing;
  2. use Illuminate\Support\ServiceProvider;
  3. class HashServiceProvider extends ServiceProvider
  4. {
  5. /**
  6. * 如果延时加载,$defer 必须设置为 true 。
  7. *
  8. * @var bool
  9. */
  10. protected $defer = true;
  11. /**
  12. * Register the service provider.
  13. *
  14. * @return void
  15. */
  16. public function register()
  17. {
  18. $this->app->singleton('hash', function () {
  19. return new BcryptHasher;
  20. });
  21. }
  22. /**
  23. * Get the services provided by the provider.
  24. *
  25. * @return array
  26. */
  27. public function provides()
  28. {
  29. return ['hash'];
  30. }
  31. }

当我们「make」时,才会注册 HashServiceProvider,即执行它的 register() 方法,进行 hash 的绑定:

  1. class MyController extends Controller
  2. {
  3. public function test()
  4. {
  5. // 此时才会注册 `HashServiceProvider`
  6. $hash = $this->app->make('hash');
  7. $hash->make('teststring');
  8. // 或
  9. \Hash::make('teststring');
  10. }
  11. }

以上就是 Laravel Service Provider 概念的简单介绍

服务容器

从容器中解析一个类

使用 Laravel 从容器中解析一个类非常简餐,有几种方法可以完成

  1. // Using the App Facade
  2. App::make('Slack\Api');
  3. // Using the app helper function
  4. app('Slack\Api');
  5. // Using the resolve helper function
  6. resolve('Slack\Client');
  1. <?php
  2. namespace Slack;
  3. class Api
  4. {
  5. public function __construct(Class $dependency)
  6. {
  7. $this->dependency = $dependency;
  8. }
  9. }

Container 会注意到我们的构造函数中有一个类型依赖,会尝试解决该依赖关系并将其传递给类

传递参数帮助我们解析

在解析容器的过程中, 我们根据类依赖来解析出相应的类, 但是有些方法需要特定的参数来进行解析或者创建, 这样便要求我们再创建类的过程中需要传递相应的参数, 例如 FormRequest

  1. $this->app->resolving(FormRequest::class, function ($request, $app) {
  2. $request = FormRequest::createFrom($app['request'], $request);
  3. $request->setContainer($app)->setRedirector($app->make(Redirector::class));
  4. });

这个类在解析的过程中需要两个参数做支持, 所以我们在自己创建这个类的时候需要传递两个参数

  1. $request = app(PamRoleRequest::class, [(app(Request::class))->replace([
  2. 'title' => 'role-be-' . $this->faker()->lexify(),
  3. 'type' => PamAccount::TYPE_BACKEND,
  4. ]), $this->app]);

为了更方便的理解这个 make 方法, Laravel 提供了一个更容易理解的方法 App::makeWith, 语义上更容易理解.

参考