初探laravel-facade实现

Facade 门面模式定义:

门面模式(Facade)又称外观模式,用于为子系统中的一组接口提供一个一致的界面。
门面模式定义了一个高层接口,这个接口使得子系统更加容易使用:引入门面角色之后,
用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度。

在 laravel 中,使用 facade 可以提高很高的便利性,比如我们使用缓存,可用下述代码实现

1
2
3
4
5
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
return Cache::get('key');
});

那么,在 laravel 中,是如何做到这个的呢?为什么可以直接使用静态调用呢?以 cache 为例,我们一探究竟。

调用方代码

1
2
3
4
5
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
return Cache::get('key');
});

我们再看看 vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Illuminate\Support\Facades;

class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}

我们发现继承了 vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.phpabstract class Facade,并且重写了 getFacadeAccessor()

接下来,我们摘录 abstract class Facade 中几处有意思的代码,注意看我的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace Illuminate\Support\Facades;

abstract class Facade
{
// 当调用 Cache::get() 时,由于 Facade 类本身没有 get 这个静态方法,所以会执行 __callStatic
public static function __callStatic($method, $args)
{
// 看 Illuminate\Support\Facades\Cache 中实现的,就是返回了 cache 字符串,其实就是服务名称
$instance = static::getFacadeRoot();

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

// 执行服务对应的方法,即: vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php 的方法
return $instance->$method(...$args);
}

public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}

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 时,其实就是直接调用的相关服务的某个方法。只不过写法上是
静态方法,然后继承了基础的 vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php,由于静态方法不存在,
自动调用魔术方法 __callStatic,在魔术方法中进行了查找对应服务,并执行对应的方法。

看下图中蓝色标注的部分,像不像个代理?其实在 laravel 官方文档 中正有此介绍

Facades provide a “static” interface to classes that are available in the application’s service container.
Laravel ships with many facades which provide access to almost all of Laravel’s features.
Laravel facades serve as “static proxies” to underlying classes in the service container,
providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than
traditional static methods