容器类是laravel的一个核心功能类,通过分解将了解核心功能。
Container 头部声明
namespace Illuminate\Container;
use Closure; // "PHP 默认闭包类"
use Exception; // "异常处理类"
use ArrayAccess; // "对象按数组方式调用类"
use LogicException;
use ReflectionClass; // "反射类"
use ReflectionParameter; // "反射参数类"
use Illuminate\Support\Arr; // "边界数组访问类"
use Illuminate\Contracts\Container\BindingResolutionException; // "绑定结果异常类"
use Illuminate\Contracts\Container\Container as ContainerContract; // "容器契约"
class Container implements ArrayAccess, ContainerContract
容器实现了ArrayAccess接口兼容以数组调用的方式来读取属性。
成员变量
Container 的成员
protected static $instance; // "Application 实例"
protected $resolved = []; // "已解析的类型的数组"
protected $bindings = []; // "容器中的绑定数组"
protected $methodBindings = []; // "容器中的方法绑定"
protected $instances = []; // "容器中的共享实例"
protected $aliases = []; // "类的别名"
protected $abstractAliases = []; // "类的别名"
protected $extenders = []; //
protected $tags = []; // "所有注册的标签"
protected $buildStack = []; // "build进行中的具体类"
protected $with = []; // "build中需要的构造函数参数"
public $contextual = []; // "构建类的上下文环境"
protected $reboundCallbacks = [];
protected $globalResolvingCallbacks = []; // "容器全局需要在解析对象过程中调用的回调"
protected $globalAfterResolvingCallbacks = []; // "容器全局需要在解析后调用的回调"
protected $resolvingCallbacks = []; // "针对指定的类在解析的时候调用的回调"
protected $afterResolvingCallbacks = [];// "针对指定的类在解析后的时候调用的回调"
主要方法解析
绑定抽象名和具体类的方法
public function bind($abstract, $concrete = null, $shared = false)
{
// "从 $instances 和 $aliases 清理旧的绑定信息"
$this->dropStaleInstances($abstract);
// "如果没有传入具体类,则将抽象名赋值给具体类"
if (is_null($concrete)) {
$concrete = $abstract;
}
// "如果具体类不是闭包对象,则进行一层包装"
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
// "将闭包对象$concrete和$shared赋值到bindings中的抽象名下"
$this->bindings[$abstract] = compact('concrete', 'shared');
// "如果抽象名已经被解析过实例,则进行重新绑定"
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
根据给定的类获取实例
public function build($concrete)
{
// "如果是闭包则直接执行自己获取实例"
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
// "获取具体类的反射"
$reflector = new ReflectionClass($concrete);
// "如果类不可以实例化则抛出异常"
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
// "将类放入构建堆栈中"
$this->buildStack[] = $concrete;
// "获取类的构造函数"
$constructor = $reflector->getConstructor();
// "如果没有构造函数,直接从 buildStack 堆栈中 pop 出的同时返回新对象"
if (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete;
}
// "获取构造函数依赖变量"
$dependencies = $constructor->getParameters();
// "解析所有需要的变量包括对象 (DI就是这么实现的!)"
$instances = $this->resolveDependencies(
$dependencies
);
// "弹出堆栈中待构建的类"
array_pop($this->buildStack);
// "根据参数实例化类并返回"
return $reflector->newInstanceArgs($instances);
}
根据传入的抽象名和参数解析出相对的具体类
protected function resolve($abstract, $parameters = [])
{
// "如果存在别名则返回,否则返回抽象"
$abstract = $this->getAlias($abstract);
// "存在参数或者给定抽象的上下文绑定则复制给变量"
// "上下文绑定实现指 $abstractAliases 中的绑定关系"
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
// "如果是个单例实例则可以直接返回已经存在的实例。"
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
// "将需要的参数放入 $with 中"
$this->with[] = $parameters;
// "从$bindings中获取抽象对应的具体类"
$concrete = $this->getConcrete($abstract);
if ($this->isBuildable($concrete, $abstract)) {
// "如果具体类是闭包或者和抽像同名的时候直接实例化这个类"
$object = $this->build($concrete);
} else {
// "递归套嵌的依赖,知道所有的依赖被解析实例化。"
$object = $this->make($concrete);
}
// "如果此类定义了拓展,则将该构建中的对象进行拓展和修饰。这允许更改配置或者修饰对象"
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
// "如果是单例类,直接将实例放入内存,防止后续重复实例化。"
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
// "调用所有全局待解析的回调和指定抽象的回调"
$this->fireResolvingCallbacks($abstract, $object);
// "标识此抽象已经被解析完毕。"
$this->resolved[$abstract] = true;
// "with中的数据出栈"
array_pop($this->with);
// "返回解析完毕的实例"
return $object;
}
总结
目前只是分析比较核心的方法
bind() 方法负责把抽象和具体类绑定到$bindings成员中。
build() 方法负责具体类中反射出需要的参数构造实例并返回。
resolve()方法则是将抽象从$aliases中查找别名,
然后从$bindings获取具体类,最后调用build()方法来构造类,
或者继续递归自己来构造类的依赖。其中还包含拓展类以及执行所有回调。
同时如果是单例的类则在构造完成后存入$instances,
下次调用则直接从$instances返回实例。