Facades(读音:/fəˈsäd/ )为应用程序的 服务容器 中可用的类提供了一个「静态」接口。你不必 use 一大串的命名空间,也不用实例化对象,就能访问对象的具体方法。


use Config;class Test
{public function index(){return Config::get('app.name');}
}

Facade 的启动与注册

Facade 的启动引导是在 Illuminate\Foundation\Bootstrap\RegisterFacades 中注册的。


public function bootstrap(Application $app)
{Facade::clearResolvedInstances();Facade::setFacadeApplication($app);AliasLoader::getInstance(array_merge($app->make('config')->get('app.aliases', []),$app->make(PackageManifest::class)->aliases()))->register();
}

默认的别名配置是从 app 配置文件下的 aliases 读取的,PackageManifest 是 laravel 5.5 新增的 包自动发现规则,这里我们暂时不考虑 PackageManifest 包提供的别名。

其中,array_merge 返回如下格式的数组:

"App" => "Illuminate\Support\Facades\App""Artisan" => "Illuminate\Support\Facades\Artisan""Auth" => "Illuminate\Support\Facades\Auth""Blade" => "Illuminate\Support\Facades\Blade"...

上面代码将通过 AliasLoader 把所有的 facade 注册进自动加载。其核心就是 php 的 spl_autoload_register

/*** Prepend the load method to the auto-loader stack.** @return void*/protected function register(){if (! $this->registered) {spl_autoload_register([$this, 'load'], true, true);$this->registered = true;}}

注册完成后,后续所有 use 的类都将通过 load 函数来完成类的自动加载。

注意,这里在定义 spl_autoload_register 时,最后面的参数传的是 true。当该参数是 true 时,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。(优先通过该函数来完成自动加载)

也就是说,

<?phpuse Config;
use App\User;class Test
{public function index(){Config::get('app.name');new User();}
}

不管我们 use 的是具体存在的类(App\User)还是别名 (Config),都将最先通过 load 函数来完成自动加载,当该函数返回 false 时,再由其他自动加载函数来完成自动加载(如 composer psr-4)。

在 AliasLoader 的 load 方法中,主要是用了 class_alias 函数来实现的别名自动加载。


public function load($alias)
{if (isset($this->aliases[$alias])) {return class_alias($this->aliases[$alias], $alias);}
}

关于 class_alias 这里帖一个官方的列子:

class foo { }class_alias('foo', 'bar');$a = new foo;
$b = new bar;// the objects are the same
var_dump($a == $b, $a === $b); //true
var_dump($a instanceof $b);    //false// the classes are the same
var_dump($a instanceof foo);   //true
var_dump($a instanceof bar);   //truevar_dump($b instanceof foo);   //true
var_dump($b instanceof bar);   //true

Facade 的加载

当我们在使用 Facade 时,如:

<?phpuse Config;class Test
{public function index(){Config::get('app.name');}
}

实际上加载的是 Illuminate\Support\Facades\Config 类(因为我们已经注册了 class_alias),相当于:

<?phpuse Illuminate\Support\Facades\Config;class Test
{public function index(){Config::get('app.name');}
}

而所有的 Facade 都继承自 Illuminate\Support\Facades\Facade 类,在该基类中定义了一个 __callStatic 方法,已至于我们能够轻松地使用 Facade(不用实列化)。

<?phppublic static function __callStatic($method, $args)
{$instance = static::getFacadeRoot();if (! $instance) {throw new RuntimeException('A facade root has not been set.');}return $instance->$method(...$args);
}

getFacadeRoot 方法用于获取别名类的具体实列,我们知道,所有的 Facade 类都需要定义一个 getFacadeAccessor 方法。该方法可能的返回值有:

  1. String 类型的字符串(如 configdb
  2. String 类型的类字符串 (如 App\Service\SomeService
  3. Object 具体的实列化对象
  4. Closure 闭包

如 Config Facade 的 getFacadeAccessor 方法如下:

protected static function getFacadeAccessor()
{return 'config';
} 

getFacadeRoot 方法将根据 getFacadeAccessor() 的返回值,从容器从取出对应的实列对象。

public static function getFacadeRoot()
{$name = static::getFacadeAccessor();if (is_object($name)) {return $name;}if (isset(static::$resolvedInstance[$name])) {return static::$resolvedInstance[$name];}return static::$resolvedInstance[$name] = static::$app[$name];
}

由于 APP 容器中已经注册过 config 的实列

<?php
//Illuminate\Foundation\Bootstrap/LoadConfiguration$app->instance('config', $config = new Repository($items));

所以 \Config::get('app.name', 'dafault) 实际访问的是 Repository 实列的 get('app.name', 'default') 方法。

参考文献:

https://segmentfault.com/a/1190000011274118

Laravel Facade的加载过程及原理相关推荐

  1. Window7系统的完整开机加载过程的原理和机制

    1.开启电源 计算机系统将进行加电自检(POST).如果通过,之后BIOS会读取主引导记录(MBR)--被标记为启动设备的硬盘的首扇区,并传送被Windows 7建立的控制编码给MBR.这时,Wind ...

  2. java虚拟机学习(四)类的加载过程

    2019独角兽企业重金招聘Python工程师标准>>> 类从虚拟机内存加载到从内存卸载,经历的生命周期是:加载,验证,准备,解析,初始化,使用,卸载这几个阶段, 其中验证,解析,初始 ...

  3. 面试官:讲讲Spring框架Bean的加载过程

    spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步. 不过作为朝夕相处的框架,我们必须得明白一个问题就是spri ...

  4. JVM源码阅读-本地库加载流程和原理

    前言 本文主要研究OpenJDK中JVM源码中涉及到native本地库的加载流程和原理的部分.主要目的是为了了解本地库是如何被加载到虚拟机,以及是如何找到并执行本地库里的本地方法,以及JNI的 JNI ...

  5. JVM学习笔记之-类加载子系统,类的加载与类的加载过程,双亲委派机制

    一 类加载器与类加载过程 类加载子系统作用 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于 ...

  6. JVM从入门到精通(三):热加载的实现原理,Java内存模型,缓存行,指令重排,合并写技术等

    上节回顾:类加载机制 双亲委派机制 parent只是一个成员变量,不是继承关系. 上节课的遗留问题 parent是怎么指定的? 手动指定parent: 双亲委派机制可以被打破吗? 双亲委派机制是在Cl ...

  7. 中yeti不能加载_第二十章_类的加载过程详解

    类的加载过程详解 概述 在 Java 中数据类型分为基本数据类型和引用数据类型.基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载 按照 Java 虚拟机规范,从 Class 文件到加载到内 ...

  8. 你所不知道的SQL Server数据库启动过程(用户数据库加载过程的疑难杂症)

    转http://www.cnblogs.com/zhijianliutang/p/4100103.html 前言 本篇主要是上一篇文章的补充篇,上一篇我们介绍了SQL Server服务启动过程所遇到的 ...

  9. Trembling ! Java类的加载过程详解(加载验证准备解析初始化使用卸载)

    [1]类的生命周期 一个类从加载进内存到卸载出内存为止,一共经历7个阶段: 加载->验证->准备->解析->初始化->使用->卸载 其中,类加载包括5个阶段: 加载 ...

最新文章

  1. 《UNIXLinux程序设计教程》一3.7 非阻塞I/O
  2. 数据中心冷热空气流控制优化方案
  3. [MATLAB学习笔记]clf清空图窗1013(1)
  4. 为什么Redux需要reducer成为“纯函数”
  5. WebRTC促进跨平台指挥调度,触发安防应用新创意
  6. mongodb 速成笔记
  7. 《Summer Tree》第八期封面
  8. 16复变函数的积分(二)
  9. python基础代码大全-Python基础汇总
  10. 信息系统项目管理笔记
  11. 数据挖掘概念与技术第三版 范明、孟晓峰译 第三章习题答案
  12. 白话大数据与机器学习——阅读笔记
  13. 戴尔计算机无法安装Win10,dell电脑安装win10 识别不了硬盘
  14. 修改bios密码 服务器,为BIOS设置密码让我们的系统更加安全
  15. VI设计创意的方法与技巧
  16. 基于java+ssm+mysql的医院管理系统
  17. ubuntu20.04 root用户 登录桌面 / kubuntu20.04 root用户 登录桌面
  18. Java媒体框架(JMF)资料
  19. 【00】processing-历史(中文)
  20. Windows 系统错误193: 0xc1

热门文章

  1. FizzBuzz(二)
  2. 多渠道推广场景下,如何实现 App 用户增长的精准归因?
  3. 《Mastering the game of Go with deep neural networks and tree search》解读
  4. 法国程序员没有“996” 40多岁最受人尊敬
  5. VM16虚拟机去虚拟化心得3(干货来了)
  6. STM32移相全桥电源方案
  7. RecyclerView 横向滑动
  8. Federated Learning of Multi-branch Networks from Periodically Shifting Distributions
  9. python 素描化图像_任意图像转素描:Python分分钟实现
  10. 华为s5735交换机配置ssh远程登陆