上篇:Laravel源码入门-启动引导过程(八)HandleExceptions
上文介绍了 HandleExceptions,在 《Laravel源码入门-启动引导过程(五)$kernel->handle($request)》中第四个要载入的是 RegisterFacades,也就是 Foundation\Http\Kernel::bootstrapers[] 的第四个
\Illuminate\Foundation\Bootstrap\RegisterFacades::class, 如下:
// Illuminate\Foundation\Http\Kernel.php 片段
/**
* The bootstrap classes for the application.
* 引导类,起引导作用的类
*
* @var array
*/
protected $bootstrappers = [
// 载入服务器环境变量(.env 文件)
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
// 载入配置信息(config 目录)
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
// 配置如何处理异常
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
// 注册 Facades
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
// 注册 Providers
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// 启动 Providers
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
我们再直接贴出 RegisterFacades 类的代码,进行分析,非常直观,如下
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
}
}
代码虽然直观,但是对于Laravel的初学者,有个问题是难以理解它的一些概念,Facade就是其一。少于程序语言能够做这样的概念的重构,有点做博士论文的味道,以至于我们所说的Laravel的学习曲线长,主要就是因为学术味道过于浓重,无法上手就用。
来看看Facade,collins词典的解释是:The facade of a building, especially a large one, is its front wall or the wall that faces the street.用来说明建筑,那么建筑的facade是什么呢,是他的前墙面,所谓”前墙面“实际是临街的一面,很清楚了,通俗说建筑的外立面。
谷歌出来的Facade的图片大多是这里图片呈现的样子,可以看到,建筑的外立面是需要重点装饰的,以至于我们可以通过说某个建筑外面立的样子来明显定位他。下面具体说。我们来看看大家举例子时,对于facade的经典例程,如下:
Cache::get('akey');
先说这句代码的意思,表面看是我们一般理解的,Cache类的一个静态方法get(),返回的是通过get()获取键值为‘akey’的值。但是实际上,这里的Cache不是缓存类,get()也不是Cache类的静态方法,甚至不是Cache类的方法。那到底是什么呢?
我们接着外立面的概念,虚拟一个类吧,Illuminate\Foundation\Building,建筑物类,假如是 laravel 的 Illuminate的基础类中的一个类。再虚拟一个类,Illumniate\Supports\Facades\Building,这个不叫建筑物类了,我们把它叫做建筑物外立面类。它位于 Illuminiate支持中的 Facades中。如果说我们要写得更清晰的话,可以使用这样写:
Illuminate\Foundation\BuildingFoundation;
Illumniate\Supports\Facades\BuildingFacade;
当然因为有 namespace 的使用,我们就不用谢后缀的同义反复了。粗浅理解,我们本身有BuildingFoundation,再给建筑外面加个壳子,就成为BuildingFacade,当都建成以后,我们再去称呼他们时,往往会用 他的外壳,或者说外观的样子来做代词指
代,比如 鸟巢和水立方,实际上他们是国家体育场(http://www.n-s.cn/)和国家游泳中心(www.water-cube.com/cn)的Facade。顺理成章了,当我们以 BuildFacade::get()使用时,实际上BuildFacade没有这个方法,而是调用的 BuildFoundation的get()方法。
这样做有什么好处呢,一般认为是便于可测试性与降低耦合性,说得直白一些,就像说鸟巢和水立方,比说国家体育场和国家游泳中心更方便,和更具有好的辨认性一样。更进一步,在复杂的应用容器中,对象如星空浩繁,数量庞大,大项目更是如此,如何那么容易就能提取出、表达出某个对象呢,靠传参数、传指针的方式,已经让我们搞得晕头转向,到不如,给他做个壳子(Facade)让他更好提取出来,所有耦合性也就降低了。实际上,真正的耦合性没有降低,或者说,根本不是降低了真正的业务逻辑上的耦合性,而是laravel找到一个简便有效的办法可以访问需要访问的功能或对象而已。
总结一句,BuildingFacade 更容易地解析(make())出需要的对象 BuildingFoundation。
=======
了解了 Facade,让我们再次回到具体 的 RegisterFacades 代码清单:
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
// 第一步:清空所有已经解析的实例,源代码就一句:
// static::$resolvedInstance = [];见 Illuminate\Support\Facades\Facade.php
Facade::clearResolvedInstances();
// 第二步:设置 $app,源代码也是一句:
// static::$app = $app;
Facade::setFacadeApplication($app);
// 第三步:获取应用配置信息->获取Facade类别名->获取别名下的实例->注册实例
// 下面是源代码,我们格式化一下,便于阅读和查看:
// AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
// 3.1 获取配置信息库(Illuminate\Cache\Repository.php)
$config = $app->make('config');
dump($config);
// 3.2 获取配置库中的 app.aliases 所有Facade别名
$aliases = $config->get('app.aliases', []);
dump($aliases);
// 3.3 有Facade别名获取别名实例(如果没有就创建:new static())
$instance = AliasLoader::getInstance($aliases);
// 查看注册前的 $instance。
dump($instance);
// 3.4 注册实例(看源码使用了spl_autoload_register([$this, 'load'], true, true);)
$instances->register();
// 查看注册后的 $instance。
dump($instance);
}
}
dump($config)结果:
dump($aliases)结果:所有的Facades(建筑的外立面)
注册前后dump($instance)结果:
附录:Facade的官方介绍:https://laravel.com/docs/5.4/facades
反思:说到Facade之于我们来说,就是穴位之于中医。中医不像西医,不知道也不想知道身体内的具体影像、或很细微的结构的东西,他就通过经脉,通过把脉知道病情,通过按摩具体穴位实现治愈功能。除此,Facade之于我们,也更像武林高手,点穴即可!点!点!Facade::xxx()!
反思:例子拓展:Facade都位于 Illuminate\Support\Facades\
仿造:https://laravel.com/docs/5.4/facades#facade-class-reference
Facade | Class | Service Container Binding | 备注 |
---|---|---|---|
NationalStadium | Illuminate\Foundation\NationalStadium | birdsnest | http://www.n-s.cn/ |
NationalAquaticsCenter | Illuminate\Foundation\NationalAquaticsCenter | watercube | http://www.water-cube.com/cn/ |