Laravel启动分析(四) 服务提供者获取

147 阅读2分钟

上面我们已经了解了服务提供者的启动,注册等流程,接下来我们来看看Laravel是怎么获取到服务提供者的。上一张在启动服务的时候,启动了服务提供者RegisterFacades,下面我们来看看他的流程


//Illuminate/Foundation/Bootstrap/RegisterFacades.php
class RegisterFacades
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);
        //获取到app.config文件的别名方法
        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}

//Illuminate/Foundation/AliasLoader.php
public static function getInstance(array $aliases = [])
{
    if (is_null(static::$instance)) {
        return static::$instance = new static($aliases);
    }

    $aliases = array_merge(static::$instance->getAliases(), $aliases);

    static::$instance->setAliases($aliases);

    return static::$instance;
}

public function register()
{
    if (! $this->registered) {
        $this->prependToLoaderStack();

        $this->registered = true;
    }
}

protected function prependToLoaderStack()
{   
    //查看load方法
    spl_autoload_register([$this, 'load'], true, true);
}


public function load($alias)
{
    if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
        $this->loadFacade($alias);

        return true;
    }
    //$aliases配置里的Facade类创建了对应的别名
    if (isset($this->aliases[$alias])) {
        return class_alias($this->aliases[$alias], $alias);
    }
}

通过上面我们已经知道把$aliases配置里的Facade类创建了对应的别名,Laravel将使用这个别名来获取到服务提供者,接下来我们看看通过别名来获取到服务提供者的流程,我们已常见的Route服务提供者为例讲解


Route::get("/hello","Controller@index");

class Route extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    //这里只有一个方法,并没有什么执行的方法,我们继续查看父类中是否有执行的方法
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
}

//父类执行__callStatic方法 当调用不存在的方法时,调用类中的魔术方法
public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return $instance->$method(...$args);
}

public static function getFacadeRoot()
{
    return static::resolveFacadeInstance(static::getFacadeAccessor());
}
//static::getFacadeAccessor() 延迟绑定 执行子类的方法 获取到返回值为route

//获取到对应的服务提供者
protected static function resolveFacadeInstance($name)
{
    if (is_object($name)) {
        return $name;
    }

    if (isset(static::$resolvedInstance[$name])) {
        return static::$resolvedInstance[$name];
    }
    // 获取到服务提供者
    return static::$resolvedInstance[$name] = static::$app[$name];
}

通过上面的门面类我们已经获取到了服务提供者,下面我们来看看门面模式的简单介绍


/**
 * 外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式。
 * 
 * 举例:客服端需要造一辆车,需要会调用方向盘,轮胎,油门等,客户端这个时候需要实例化三个对象
 * 如果采用门面模式,由门面去处理的话,客户端只需要调用门面即可
 */
class Throttle
{
    public function __construct()
    {
        echo "创建油门";
    }
}

class Tyre
{
    public function __construct()
    {
        echo "创建轮胎";
    }
}

class Steering
{
    public function __construct()
    {
        echo "创建方向盘";
    }
}


class Clinet {
    private $throttle;
    private $tyre;
    private $steering;

    /**
     * 存在一个问题,如果再次增加一个方法的话,那么
     * 客户端也会再次增加一个方法,耦合性太强,使用门面呢
     * Clinet constructor.
     */
    public function __construct()
    {
        $this->steering = new Steering();
        $this->tyre = new Tyre();
        $this->throttle = new Throttle();
    }
}

class Facade {
    private $throttle;
    private $tyre;
    private $steering;

    /**
     * 存在一个问题,如果再次增加一个方法的话,那么
     * 客户端也会再次增加一个方法,耦合性太强,使用门面呢
     * Clinet constructor.
     */
    public function __construct()
    {
        $this->steering = new Steering();
        $this->tyre = new Tyre();
        $this->throttle = new Throttle();
    }
}


class ClinetFacade {
    //使用门面模式的话,即使内部改变了,我们也只需要改变
    //门面里面的东西,而客户端是不用改变的
    private $facade;
    public function __construct()
    {
        $this->facade = new Facade();
    }
}

门面模式 一般只修改门面内部,而不修改调用者