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

作者: AntFoot | 来源:发表于2017-05-12 12:26 被阅读858次

    1. 服务提供者配置:

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

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

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

    3. 注册服务提供者流程

    3.1 注册流程的过程大概
    • 3.1.1 入口文件index.php开始执行代码

    • 3.1.2 引入自动加载文件脚本
      // 查看该autoload.php文件,引入了composer安装的vendor的自动加载文件
      require DIR.'/../bootstrap/autoload.php';

    • 3.1.3 引入应用程序,在应用程序中实例化了应用中心类Application,执行了一下初始化操作。其中包括事件,日志和路由的服务提供者的注册
      $app = require_once DIR.'/../bootstrap/app.php';

    • 3.1.4 获取laravel内核Kernel类实例
      $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

    • 3.1.5 执行Kernel类中的handle方法处理HTTP请求,并返回数据。执行此方法的之前,获取了Request的实例,并作为参数传进来。Kernel作为Laravel的内核部分。接受HTTP请求,引用应用程序处理HTTP请求,最后返回Reponse
      $response = $kernel->handle(
      $request = Illuminate\Http\Request::capture()
      );

    3.2 启动应用时,容器的初始化详情
    • 3.2.1 创建Application实例
      Application是Laravel的应用中心并且继承了容器Container类
      $app = new Illuminate\Foundation\Application(
      realpath(DIR.'/../')
      );

    • 3.2.2 构造函数初始化

            public function __construct($basePath = null)
            {
                if ($basePath) {
                      // 设置应用基本路径
                      $this->setBasePath($basePath);
            }
                // 注册基本绑定
                $this->registerBaseBindings();
                // 注册基本服务提供者,包括事物,日志和路由
                $this->registerBaseServiceProviders();
                // 注册核心的容器别名
                $this->registerCoreContainerAliases();
            }
      

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

    • 3.2.3 注册基本服务提供者。用一种委托模式,将需要注册的三个服务提供者实例化,作为参数传进Application中的register方法
      protected function registerBaseServiceProviders()
      {
      $this->register(new EventServiceProvider($this));
      $this->register(new LogServiceProvider($this));
      $this->register(new RoutingServiceProvider($this));
      }

    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;
      }
    
    • 3.2.4 RoutingServiceProvider()中registert方法完成具体注册操作
      public function register()
      {
      $this->registerRouter();
      $this->registerUrlGenerator();
      $this->registerRedirector();
      $this->registerPsrRequest();
      $this->registerPsrResponse();
      $this->registerResponseFactory();
      }
      共享注册路由服务相关的各个实例绑定到容器。主要是通过app中的singleton方法来实现

    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()方法。调用被注册过的服务提供者,实现具体功能

    相关文章

      网友评论

        本文标题:Laravel 服务提供者是如何注册的

        本文链接:https://www.haomeiwen.com/subject/pwdztxtx.html