PHP经验分享程序员程序猿阵线联盟-汇总各类技术干货

Laravel 服务提供者是如何注册的

2017-05-12  本文已影响858人  AntFoot

1. 服务提供者配置:

 服务提供者是Laravel核心配置,在项目的config/app.php文件中的providers数组中,配置了启动应用所要加载的所有服务提供者类,其中很多是延迟加载的,也就是说不是每次请求都会被加载,只有真的用到它们的时候才会加载。这样的服务提供者类中有$defer决定。$defer=true。该服务提供者表示延迟加载。
   /**
   * 服务提供者加是否延迟加载.
   *
   * @var bool
   */
  protected $defer = true;

2. 服务提供者主要方法:

  register: 绑定事物到容器(本文大篇幅分析register的执行过程)
  boot: 调用被注册的服务提供者,实现具体功能

3. 注册服务提供者流程

3.1 注册流程的过程大概
3.2 启动应用时,容器的初始化详情

设置应用基本路径:
注册基本绑定
注册基本服务提供者,包括事物,日志和路由
注册核心的容器别名

Application中的register方法是该委托模式下,执行传进来的服务提供者实例中自身的register方法。进而实现注册

  public function register($provider, $options = [], $force = false)
  {
      // 检查当前服务提供者实例已经注册则直接返回
      if (($registered = $this->getProvider($provider)) && ! $force) {
          return $registered;
      }
      // 如果当前$provider是一个类名的字符串,则实例化该类
      if (is_string($provider)) {
          $provider = $this->resolveProvider($provider);
      }
      // 如果当前实例中存在register方法则调用该方法
      if (method_exists($provider, 'register')) {
          $provider->register();
      }
      // 标记服务提供者已注册状态
      $this->markAsRegistered($provider);
      if ($this->booted) {
          $this->bootProvider($provider);
      }
      return $provider;
  }

singleton方法的功能是注册实例,绑定到容器
调用该方法通常会传两个参数:$abstract, $concrete。通过容器中的bind()方法,将这两个参数以key-value的形式绑定到容器中的$bindings数组中。之后需要用到通过此方法绑定的实例。可以通过make方法获取

3.3 绑定一些重要的接口。共享到容器

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
$app->singleton(
      Illuminate\Contracts\Console\Kernel::class,
      App\Console\Kernel::class
);
$app->singleton(
      Illuminate\Contracts\Debug\ExceptionHandler::class,
      App\Exceptions\Handler::class
);  

3.4 获取内核Kernel的实例

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

3.2 中提到singleton方法的作用,此处获取的Kernel实例就是在3.3步中绑定的重要接口之一

  $app->singleton(
      Illuminate\Contracts\Http\Kernel::class,
      App\Http\Kernel::class
  );

3.5 处理HTTP请求

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

执行App\Http\Kernel类中的handle方法。查看App\Http\Kernel类是继承了Illuminate\Foundation\Http\Kernel类,该类中定义了handle方法,用于处理传进来的HTTP请求

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    event(new Events\RequestHandled($request, $response));

    return $response;
}

查看代码,执行Kernel中的sendRequestThroughRouter方法,通过中间件或是路由发送传进来的请求到处理程序

protected function sendRequestThroughRouter($request)
{
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

在sendRequestThroughRouter方法中执行了bootstrap方法,这个方法就是我们要找的,即引导应用程序来处理HTTP请求

public function bootstrap()
{
     // 判断应用程序之前是否已被引导
    if (! $this->app->hasBeenBootstrapped()) {
         // 如何没有被引导,则引导程序注册应用程序需要被引导的数组
        $this->app->bootstrapWith($this->bootstrappers());
    }
}

其中注册所有服务提供者的引导类就在该$this->bootstrappers()方法返回的数组中

protected $bootstrappers = [
      \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
];

即,\Illuminate\Foundation\Bootstrap\RegisterProviders::class

3.6 注册服务提供者

通过Application中的bootstrapWith方法执行各个引用类的bootstrap方法。注册服务提供者即在此被执行

//Illuminate\Foundation\Bootstrap\RegisterProviders
public function bootstrap(Application $app)
{
    $app->registerConfiguredProviders();
}

调用app中的registerConfiguredProviders注册所有服务提供者

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

load方法加载config/app.php配置文件中的providers数组的内容。查看其内容,找到$this->app->register($provider)。将providers数组中的各个服务提供者完成注册。
在app的register()方法中

    // 标记服务提供者已注册状态
    $this->markAsRegistered($provider);
    protected function markAsRegistered($provider)
    {
      $this->serviceProviders[] = $provider;
      $this->loadedProviders[get_class($provider)] = true;
    }

将所有服务提供者注册完成之后,保存在$serverProviders数组中,等待引导方法,即boot()方法。调用被注册过的服务提供者,实现具体功能

上一篇下一篇

猜你喜欢

热点阅读