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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 先说几句废话,调和气氛。事情的起由来自客户需求频繁变更,伟大的师傅决定横刀立马的改革使用新的框架(created ...
    wsdadan阅读 3,033评论 0 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 入门laravel 初始化流程 http 的初始化 引入composer的autoload 实例化Applocat...
    lerko_阅读 1,236评论 2 9
  • 开始大学的第一个假期,而这个假期却没有想象中的美好和幸福。 也许一切已经顺其自然,形成习惯,没有作业,没有压力,也...
    吾闻阅读 423评论 3 1
  • 前情回顾 自从和许绍念重遇后,我发现他变得有些咄咄逼人,每次见面总抛出一些令我为难的问题,我私下揣测,估计是对当年...
    狗一样的污姐阅读 261评论 0 0