Laravel启动分析(三) 服务提供者启动

485 阅读3分钟

服务提供者是 Laravel 应用启动的中心,你自己的应用以及所有 Laravel 的核心服务都是通过服务提供者启动。但是,我们所谓的「启动」指的是什么?通常,这意味着注册服务,包括注册服务容器绑定、事件监听器、中间件甚至路由。服务提供者是应用配置的中心。

有了上面的依赖注入和服务容器,对于服务提供者来说,就非常好理解了,先来看一下Laravel自带的服务提供者AppServiceProvider

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {

    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
    
    }
}

其中含有两个方法,register()和boot()这两个方法一个是注册,一个是启动初始化, 通过阅读源码,我们来查看Laravel是怎么运行这两个方法的

//Illuminate/Foundation/Application.php
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();  //绑定一些基本变量

    $this->registerBaseServiceProviders(); //注册服务

    $this->registerCoreContainerAliases(); //注册别名
}

//Illuminate/Foundation/Application.php
protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}

//Illuminate/Foundation/Application.php
public function register($provider, $options = [], $force = false)
{
    //如果服务已经进行了注册,就直接返回
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    // If the given "provider" is a string, we will resolve it, passing in the
    // application instance automatically for the developer. This is simply
    // a more convenient way of specifying your service provider classes.
    //如果是个字符串,进行实例化
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
    //如果存在register方法则进行调用
    //我们上面的AppServiceProvider方法中含有register方法,因此register方法会在这里进行执行
    if (method_exists($provider, 'register')) {
        $provider->register();
    }
    //生成缓存文件
    $this->markAsRegistered($provider);

    // If the application has already booted, we will call this boot method on
    // the provider class so it has an opportunity to do its boot logic and
    // will be ready for any usage by this developer's application logic.
    // 如果应用已经启动了,即代表所有需要加载的服务提供者都已经进行了启动,可以执行boot方法
    if ($this->booted) {
        $this->bootProvider($provider);
    }
    //返回服务提供者
    return $provider;
}

通过上面的方法我们已经知道了,服务提供者的加载方式,那其他的服务提供者Laravel是怎么进行加载的呢?我们继续来看代码

//public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//执行handler方法
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

//src/Illuminate/Foundation/Http/Kernel.php
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);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}
//src/Illuminate/Foundation/Http/Kernel.php
protected function sendRequestThroughRouter($request)
{
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');
    //在这里执行了bootstrap()启动方法
    $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}
//src/Illuminate/Foundation/Http/Kernel.php
public function bootstrap()
{
    /*执行了这些服务提供者
    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,
    ];
    */
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}


//在上面注册了RegisterProviders,我们看看他的逻辑  
//Illuminate/Foundation/Bootstrap/RegisterProviders.php
public function bootstrap(Application $app)
{
    $app->registerConfiguredProviders();
}

//Illuminate/Foundation/Application.php
public function registerConfiguredProviders()
{
    //在这里加载了app.providers中的所有服务提供者
    $providers = Collection::make($this->config['app.providers'])
                    ->partition(function ($provider) {
                        return Str::startsWith($provider, 'Illuminate\\');
                    });

    $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
    
    (new ProviderRepository($this, new Filesystem, 
    //load服务提供者
    $this->getCachedServicesPath()))
                ->load($providers->collapse()->toArray());
}


//Illuminate/Foundation/ProviderRepository.php
public function load(array $providers)
{
    //加载缓存
    $manifest = $this->loadManifest();

    // First we will load the service manifest, which contains information on all
    // service providers registered with the application and which services it
    // provides. This is used to know which services are "deferred" loaders.
    if ($this->shouldRecompile($manifest, $providers)) {
        $manifest = $this->compileManifest($providers);
    }

    // Next, we will register events to load the providers for each of the events
    // that it has requested. This allows the service provider to defer itself
    // while still getting automatically loaded when a certain event occurs.
    //事件加载
    foreach ($manifest['when'] as $provider => $events) {
        $this->registerLoadEvents($provider, $events);
    }

    // We will go ahead and register all of the eagerly loaded providers with the
    // application so their services can be registered with the application as
    // a provided service. Then we will set the deferred service list on it.
    //立即加载
    foreach ($manifest['eager'] as $provider) {
        $this->app->register($provider);
    }
    //写入数组中
    $this->app->addDeferredServices($manifest['deferred']);
}


//Illuminate/Foundation/ProviderRepository.php
protected function compileManifest($providers)
{
    // The service manifest should contain a list of all of the providers for
    // the application so we can compare it on each request to the service
    // and determine if the manifest should be recompiled or is current.
    $manifest = $this->freshManifest($providers);

    foreach ($providers as $provider) {
        $instance = $this->createProvider($provider);

        // When recompiling the service manifest, we will spin through each of the
        // providers and check if it's a deferred provider or not. If so we'll
        // add it's provided services to the manifest and note the provider.
        if ($instance->isDeferred()) {
            //如果需要延迟加载,放入deffered队列中
            foreach ($instance->provides() as $service) {
                $manifest['deferred'][$service] = $provider;
            }
            //如果需要事件加载,放入when
            $manifest['when'][$provider] = $instance->when();
        }

        // If the service providers are not deferred, we will simply add it to an
        // array of eagerly loaded providers that will get registered on every
        // request to this application instead of "lazy" loading every time.
        else {
            //立即加载
            $manifest['eager'][] = $provider;
        }
    }

    return $this->writeManifest($manifest);
}


//Illuminate/Foundation/Application.php

public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        //注册事件
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
        //获取到类,执行bootstarp()方法
        $this->make($bootstrapper)->bootstrap($this);
        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    }
}
//Illuminate/Foundation/Application.php
public function make($abstract, array $parameters = [])
{
    $abstract = $this->getAlias($abstract);
    //延迟绑定
    if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
        $this->loadDeferredProvider($abstract);
    }
    //执行make方法,获取实例
    return parent::make($abstract, $parameters);
}
    
public function registerDeferredProvider($provider, $service = null)
{
    // Once the provider that provides the deferred service has been registered we
    // will remove it from our local list of the deferred services with related
    // providers so that this container does not try to resolve it out again.
    if ($service) {
        unset($this->deferredServices[$service]);
    }
    //延迟加载
    $this->register($instance = new $provider($this));
    //如果服务还未加载,放入到boot中进行加载
    if (! $this->booted) {
        $this->booting(function () use ($instance) {
            $this->bootProvider($instance);
        });
    }
}

//上面还注册了BootProviders服务,他的源码相对来说简单
public function boot()
{
    if ($this->booted) {
        return;
    }

    // Once the application has booted we will also fire some "booted" callbacks
    // for any listeners that need to do work after this initial booting gets
    // finished. This is useful when ordering the boot-up processes we run.
    $this->fireAppCallbacks($this->bootingCallbacks);
    //调用
    array_walk($this->serviceProviders, function ($p) {
        $this->bootProvider($p);
    });

    $this->booted = true;

    $this->fireAppCallbacks($this->bootedCallbacks);
}

服务提供者的流程我们大概进行了了解,接下来我们来看看怎么获取到服务提供者,这里Laravel使用了门面模式,大家有兴趣的话,可以先了解下门面模式 blog.csdn.net/qq_29058883…