Facade原理 以View::make()为例
1.调用View::make()时,会先去config/app.php中的aliases数组中找到实际调用的Facade类。
'aliases' => [
//....
'View' => Illuminate\Support\Facades\View::class,
],
2.在Illuminate\Support\Facades\View类中的getFacadeAccessor()方法会返回实际调用的对象的类或者别名。(注:可以返回带有完整命名空间的类名)
此处返回的是绑定在容器中的\Illuminate\View\Factory类的别名:
//in ViewServiceProvider.php
public function register()
{
$this->registerFactory();
$this->registerViewFinder();
$this->registerEngineResolver();
}
/**
* Register the view environment.
*
* @return void
*/
public function registerFactory()
{
$this->app->singleton('view', function ($app) {
//...
return $factory;// \Illuminate\View\Factory
});
}
3.根据返回的别名,在Facade父类中会去调用容器获取该类的实例(如果还没有实例化的话)。
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]; //这里application继承的Container类实现了ArrayAccess接口,实际调用的是其中的offsetGet()方法
}
4.容器得到$name后,由resolve方法获取实例
protected function resolve($abstract, $parameters = [])
{
$abstract = $this->getAlias($abstract);//寻找别名,没有返回原值,有的话会根据别名来获取绑定在容器中类的实例。
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
// its "nested" dependencies recursively until all have gotten resolved.
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
// If we defined any extenders for this type, we'll need to spin through them
// and apply them to the object being built. This allows for the extension
// of services, such as changing configuration or decorating the object.
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
// If the requested type is registered as a singleton we'll want to cache off
// the instances in "memory" so we can return it later without creating an
// entirely new instance of an object on each subsequent request for it.
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
// we will be ready to return back the fully constructed class instance.
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
别名(aliases, 通过application的registerCoreContainerAliases()方法设置),
没有绑定该别名则直接根据第三步中的$name实例化。
//注意,别名数组中的值是$class_name(aliases)=>$key的形式
public function registerCoreContainerAliases()
{
foreach ([
//...
'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}
注意点
- 使用时要注意命名空间的问题。
在config.php中的aliase数组以key=>value的方式记录了Facade的别名和实际调用的Facade类的对应关系。
如果不是在全局命名空间下使用Facade,需要在别名前加上’\’。