Lumen开发:lumen源码解读之初始化(2)——门面(Facades)与数据库(db)

版权声明:本文为博主原创文章,未经博主允许不得转载。

紧接上一篇

1
2
3
$app->withFacades();//为应用程序注册门面。
  
$app->withEloquent();//为应用程序加载功能强大的库。

先来看看withFacades()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
     * Register the facades for the application.(为应用程序注册门面。)
     *
     * @param  bool  $aliases
     * @param  array $userAliases
     * @return void
     */
    public function withFacades($aliases = true, $userAliases = [])
    {
        Facade::setFacadeApplication($this);
 
        if ($aliases) {
            $this->withAliases($userAliases);
        }
    }
1
setFacadeApplication()
1
2
3
4
5
6
7
8
9
10
/**
 * Set the application instance.(设置应用程序实例。)
 *
 * @param  \Illuminate\Contracts\Foundation\Application  $app
 * @return void
 */
public static function setFacadeApplication($app)
{
    static::$app = $app;
}

将当前实例传给门面类(Facade)

$this->withAliases($userAliases)

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
/**
 * Register the aliases for the application.(注册应用程序的别名。)
 *
 * @param  array  $userAliases
 * @return void
 */
public function withAliases($userAliases = [])
{
    $defaults = [
        'Illuminate\Support\Facades\Auth' => 'Auth',
        'Illuminate\Support\Facades\Cache' => 'Cache',
        'Illuminate\Support\Facades\DB' => 'DB',
        'Illuminate\Support\Facades\Event' => 'Event',
        'Illuminate\Support\Facades\Gate' => 'Gate',
        'Illuminate\Support\Facades\Log' => 'Log',
        'Illuminate\Support\Facades\Queue' => 'Queue',
        'Illuminate\Support\Facades\Schema' => 'Schema',
        'Illuminate\Support\Facades\URL' => 'URL',
        'Illuminate\Support\Facades\Validator' => 'Validator',
    ];
 
    if (! static::$aliasesRegistered) {//判断是否已注册类别名。
        static::$aliasesRegistered = true;
 
        $merged = array_merge($defaults, $userAliases);
 
        foreach ($merged as $original => $alias) {
            class_alias($original, $alias);//设置别名
        }
    }
}

 

然后就是withEloquent()函数

1
2
3
4
5
6
7
8
9
/**
 * Load the Eloquent library for the application.(为应用程序加载功能强大的库。)
 *
 * @return void
 */
public function withEloquent()
{
    $this->make('db');
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * Resolve the given type from the container.(从容器中解析给定类型。)
 *
 * @param  string  $abstract
 * @return mixed
 */
public function make($abstract)
{
    $abstract = $this->getAlias($abstract);
 
    if (array_key_exists($abstract, $this->availableBindings) &&
        ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
        $this->{$method = $this->availableBindings[$abstract]}();
 
        $this->ranServiceBinders[$method] = true;
    }
 
    return parent::make($abstract);
}

一步一步来,make()函数以后经常用得上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Get the alias for an abstract if available.(如果可用的话,获取抽象的别名。)
 *
 * @param  string  $abstract
 * @return string
 *
 * @throws \LogicException
 */
public function getAlias($abstract)
{
    if (! isset($this->aliases[$abstract])) {  //$this->aliases是注册类别名,如果没有别名,则直接返回$abstract
        return $abstract;
    }
 
    if ($this->aliases[$abstract] === $abstract) {  //如果$abstract是别名本身,则会抛出异常
        throw new LogicException("[{$abstract}] is aliased to itself.");
    }
 
    return $this->getAlias($this->aliases[$abstract]);
}

这一个的$this->aliases的值来着上一篇文章registerContainerAliases()函数对它的赋值

接下来是这段

1
2
3
4
5
6
if (array_key_exists($abstract, $this->availableBindings) &&
        ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
        $this->{$method = $this->availableBindings[$abstract]}();
 
        $this->ranServiceBinders[$method] = true;
    }
1
$this->availableBindings变量在Applilcation类的定义是是
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
37
38
39
40
41
42
43
44
/**
 * The available container bindings and their respective load methods.(可用的容器绑定及其各自的加载方法。)
 *
 * @var array
 */
public $availableBindings = [
    'auth' => 'registerAuthBindings',
    'auth.driver' => 'registerAuthBindings',
    'Illuminate\Auth\AuthManager' => 'registerAuthBindings',
    'Illuminate\Contracts\Auth\Guard' => 'registerAuthBindings',
    'Illuminate\Contracts\Auth\Access\Gate' => 'registerAuthBindings',
    'Illuminate\Contracts\Broadcasting\Broadcaster' => 'registerBroadcastingBindings',
    'Illuminate\Contracts\Broadcasting\Factory' => 'registerBroadcastingBindings',
    'Illuminate\Contracts\Bus\Dispatcher' => 'registerBusBindings',
    'cache' => 'registerCacheBindings',
    'cache.store' => 'registerCacheBindings',
    'Illuminate\Contracts\Cache\Factory' => 'registerCacheBindings',
    'Illuminate\Contracts\Cache\Repository' => 'registerCacheBindings',
    'composer' => 'registerComposerBindings',
    'config' => 'registerConfigBindings',
    'db' => 'registerDatabaseBindings',
    'Illuminate\Database\Eloquent\Factory' => 'registerDatabaseBindings',
    'encrypter' => 'registerEncrypterBindings',
    'Illuminate\Contracts\Encryption\Encrypter' => 'registerEncrypterBindings',
    'events' => 'registerEventBindings',
    'Illuminate\Contracts\Events\Dispatcher' => 'registerEventBindings',
    'files' => 'registerFilesBindings',
    'hash' => 'registerHashBindings',
    'Illuminate\Contracts\Hashing\Hasher' => 'registerHashBindings',
    'log' => 'registerLogBindings',
    'Psr\Log\LoggerInterface' => 'registerLogBindings',
    'queue' => 'registerQueueBindings',
    'queue.connection' => 'registerQueueBindings',
    'Illuminate\Contracts\Queue\Factory' => 'registerQueueBindings',
    'Illuminate\Contracts\Queue\Queue' => 'registerQueueBindings',
    'Psr\Http\Message\ServerRequestInterface' => 'registerPsrRequestBindings',
    'Psr\Http\Message\ResponseInterface' => 'registerPsrResponseBindings',
    'translator' => 'registerTranslationBindings',
    'url' => 'registerUrlGeneratorBindings',
    'validator' => 'registerValidatorBindings',
    'Illuminate\Contracts\Validation\Factory' => 'registerValidatorBindings',
    'view' => 'registerViewBindings',
    'Illuminate\Contracts\View\Factory' => 'registerViewBindings',
];
1
<br>$this->ranServiceBinders变量是记录已执行的服务绑定方法。
1
当前执行的是make('db'),所以$abstract=‘db’;
1
2
$this->availableBindings['db'] = 'registerDatabaseBindings';
$this->{$method = $this->availableBindings[$abstract]}();

会执行到Application的registerDatabaseBindings方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * Register container bindings for the application.(为应用程序注册容器绑定。)
 *
 * @return void
 */
protected function registerDatabaseBindings()
{
    $this->singleton('db', function () {  //这里是用闭包函数注册一个db的单例
        return $this->loadComponent(
            'database', [
                'Illuminate\Database\DatabaseServiceProvider',
                'Illuminate\Pagination\PaginationServiceProvider',
            ], 'db'
        );
    });
}
1
这里是用闭包函数注册一个db的单例,接着看闭包内执行了什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * Configure and load the given component and provider.(配置并加载给定的组件和提供程序。)
 *
 * @param  string  $config
 * @param  array|string  $providers
 * @param  string|null  $return
 * @return mixed
 */
public function loadComponent($config, $providers, $return = null)
{
    $this->configure($config);//将配置文件加载到应用程序中。
 
    foreach ((array) $providers as $provider) {
        $this->register($provider);//注册传过来的服务供应类
    }
 
    return $this->make($return ?: $config);//
}
1
因为这里第一次初始化后,$this->ranServiceBinders[$method]=true,所以以后调用db时,都会直接调用父类(Container)的make()函数。
1
讲远了的感觉,不过刚好可以一起讲一下最后一步return parent::make($abstract);
1
2
3
4
5
6
7
8
9
10
/**
 * Resolve the given type from the container.
 *
 * @param  string  $abstract
 * @return mixed
 */
public function make($abstract)
{
    return $this->resolve($abstract);
}

Container类的make()只调用了$this->resolve()函数,马不停蹄,我们来看看这个函数

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
     * Resolve the given type from the container.(从容器中解析给定类型。)
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    protected function resolve($abstract, $parameters = [])
    {
        $abstract = $this->getAlias($abstract);//取实际类名
 
        $needsContextualBuild = ! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        );
 
       //如果该类型的实例目前作为单例管理,
       //我们将只返回一个现有的实例而不是实例化新的实例,
       //所以开发人员每次都可以继续使用同一个对象实例。
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {  //单例已存在直接返回
            return $this->instances[$abstract];
        }
 
        $this->with[] = $parameters;
 
        $concrete = $this->getConcrete($abstract);  //返回给定抽象的具体类型(包括一些关于上下文的绑定操作)
 
        //我们已经准备好实例化绑定注册的具体类型的实例。
        //这将实例化类型,以及递归地解析所有的“嵌套”依赖关系,直到所有问题都得到解决为止。
        if ($this->isBuildable($concrete, $abstract)) {  //判断是否为递归
            $object = $this->build($concrete);  //递归用build(),用make()会死循环
        } else {
            $object = $this->make($concrete);   //非递归用make()
        }
 
        //如果我们定义了这种类型的扩展程序,
        //我们需要将它们旋转并将它们应用到正在构建的对象中。
        //这允许扩展服务,例如更改配置或装饰对象。
        foreach ($this->getExtenders($abstract) as $extender) {  //执行扩展
            $object = $extender($object, $this);
        }
 
        //如果请求的类型被注册为单例,我们将缓存“内存”中的实例,
        //以便稍后返回它,而不必为每一个后续请求创建一个对象的全新实例。
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }
 
        $this->fireResolvingCallbacks($abstract, $object);  //回调
 
        //返回之前,我们还将解析的标志设置为“true”,
        //并弹出此构建的参数重写。完成这两件事后,我们将准备返回完全构造的类实例。
        $this->resolved[$abstract] = true;
 
        array_pop($this->with);  //移除最后一个键值,也就是$this->with[] = $parameters;     return $object; <br>}

这里是整篇文比较难的地方,部分注释是直译的,讲了这么久,其实也执行了$app->withFacades()和$app->withEloquent();

其实后面的一些注册和绑定与前面也是类似的,希望能帮到大家!

Lumen技术交流群:310493206

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @   程序生(Codey)  阅读(1713)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!
点击右上角即可分享
微信分享提示