【Laravel-海贼王系列】第三章,Container 类解析

容器类是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)
    {
        $this->dropStaleInstances($abstract); // 从$instances$aliases清理旧的绑定信息

        if (is_null($concrete)) {
            $concrete = $abstract; // 如果没有传入具体类,则将抽象名赋值给具体类
        }

        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete); // 如果具体类不是闭包对象,则进行一层包装
        }

        $this->bindings[$abstract] = compact('concrete', 'shared'); // 将闭包对象$concrete$shared赋值到bindings中的抽象名下

        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(); // 获取类的构造函数

        if (is_null($constructor)) {
            array_pop($this->buildStack);

            return new $concrete; // 如果没有构造函数,直接从buildStack堆栈中pop出的同时返回新对象
        }

        $dependencies = $constructor->getParameters(); // 获取构造函数依赖变量

        $instances = $this->resolveDependencies(
            $dependencies
        ); // 解析所有需要的变量包括对象 (DI就是这么实现的!)

        array_pop($this->buildStack); // 弹出堆栈中待构建的类

        return $reflector->newInstanceArgs($instances); // 根据参数实例化类并返回
    }
复制代码
根据传入的抽象名和参数解析出相对的具体类
protected function resolve($abstract, $parameters = [])
    {
        $abstract = $this->getAlias($abstract);// 如果存在别名则返回,否则返回抽象

        $needsContextualBuild = ! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        );// 存在参数或者给定抽象的上下文绑定则复制给变量
        // 上下文绑定实现指$abstractAliases中的绑定关系

        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract]; // 如果是个单例实例则可以直接返回已经存在的实例。
        }

        $this->with[] = $parameters; // 将需要的参数放入$with$concrete = $this->getConcrete($abstract); // 从$bindings中获取抽象对应的具体类

        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; // 标识此抽象已经被解析完毕。

        array_pop($this->with);// with中的数据出栈

        return $object; // 返回解析完毕的实例
    }
复制代码

容器的核心方法基本就是这些。

bind()方法负责把抽象和具体类绑定到$bindings成员中。
复制代码
build()方法负责具体类中反射出需要的参数构造实例并返回。
复制代码
resolve()方法则是将抽象从$aliases中查找别名,
然后从$bindings获取具体类,最后调用build()方法来构造类,
或者继续递归自己来构造类的依赖。其中还包含拓展类以及执行所有回调。
同时如果是单例的类则在构造完成后存入$instances,
下次调用则直接从$instances返回实例。
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值