我们知道, 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 Provider 的 register() 方法注册「绑定」
  2. 所有 Servier Provider 的 register() 都执行完之后,再通过它们 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 Provider 中 register 和 boot 的「先后顺序了」。

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

这就意味着,在 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」 ServiceProvider 类 providers()方法中返回的值时,才会加载此 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, 语义上更容易理解.

参考