如下是Laravel官方文档的介绍laravel
Facades 为应用程序的 服务容器 中可用的类提供了一个「静态」接口。Laravel 自己附带许多的 facades,甚至你可能在不知情的情况下已经在使用他们!Laravel 「facades」做为在服务容器内基类的「静态代理」,拥有简洁、易表达的语法优势,同时维持着比传统静态方法更高的可测试性和灵活性。bootstrap
从介绍中能够看出,Facades 好处就是让代码更加简介,优雅,这也是Laravel追求的特性,如何使用Facades这里就不介绍了,能够参考Laravel文档(中文) ,本文介绍一下Facades是如何知道和建立你须要的类实例。app
以 log Facade 为例,咱们看下是如何经过log这个字符串找到 \Illuminate\Log\Writer 这个类的 先看 \Illuminate\Support\Facades\Log 门面ide
class Log extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'log'; } }
这个类很是简单,只有一个静态方法 getFacadeAccessor(), 返回了一个log字符串。 而后看Log的父类 Facade, Facede中有不少方法,这里关注其中两个:测试
abstract class Facade { /** * The application instance being facaded. * * @var \Illuminate\Contracts\Foundation\Application */ protected static $app; /** * The resolved object instances. * * @var array */ protected static $resolvedInstance; /** * Resolve the facade root instance from the container. * * @param string|object $name * @return mixed */ protected static function resolveFacadeInstance($name) { if (is_object($name)) { // 若是$name已是一个对象,则直接返回该对象 return $name; } if (isset(static::$resolvedInstance[$name])) { // 若是是已经解析过的对象,直接从$resolvedInstance中返回该对象 return static::$resolvedInstance[$name]; } return static::$resolvedInstance[$name] = static::$app[$name]; // 从容器中寻找$name对象,并放入$resolvedInstance 中以便下次使用 } /** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed * * @throws \RuntimeException */ public static function __callStatic($method, $args) // 魔术方法,当使用Log::error($msg) 的时候会调用该方法 { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } switch (count($args)) { case 0: return $instance->$method(); case 1: return $instance->$method($args[0]); case 2: return $instance->$method($args[0], $args[1]); case 3: return $instance->$method($args[0], $args[1], $args[2]); case 4: return $instance->$method($args[0], $args[1], $args[2], $args[3]); default: return call_user_func_array([$instance, $method], $args); } } }
经过以上分析知道最终Facede找的是容器中绑定的实例,因此接下来咱们找一下log是在何时被注册的,this
这时候须要关注 \Illuminate\Foundation\Http\Kernel 类,Kernel类中包括如下几个方法:spa
/** * Handle an incoming HTTP request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->fire('kernel.handled', [$request, $response]); return $response; } /** * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } /** * Bootstrap the application for HTTP requests. * * @return void */ public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } }
handle方法接收一个Request请求,并返回一个$response,$response->sendRequestThroughRouter()的时候调用了bootstrap()方法,继续看bootstrap方法里面加载了已经定义好的几个类:(这些定义都在Kernel类中)代理
/** * The bootstrap classes for the application. * * @var array */ protected $bootstrappers = [ 'Illuminate\Foundation\Bootstrap\DetectEnvironment', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'Illuminate\Foundation\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\RegisterProviders', 'Illuminate\Foundation\Bootstrap\BootProviders', ];
而后咱们看ConfigureLogging中的registerLogger()方法code
/** * Register the logger instance in the container. * * @param \Illuminate\Contracts\Foundation\Application $app * @return \Illuminate\Log\Writer */ protected function registerLogger(Application $app) { $app->instance('log', $log = new Writer( // 这里把Writer注册到了容器中 new Monolog($app->environment()), $app['events']) ); return $log; }
到此为止,咱们已经知道Facede是如何找到想要使用的类了。 Facedes看起来挺高大上,但实现起来的原理挺简单的,实际上也是一种单例模式,只不过在调用处包装了一下component