上面我们已经了解了服务提供者的启动,注册等流程,接下来我们来看看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();
}
}
门面模式 一般只修改门面内部,而不修改调用者