Skip to content

注册基础绑定

对应代码

php
    $this->registerBaseBindings();

方法实现

php
protected function registerBaseBindings()
{
    // 将容器实例绑定到静态变量$instance上
    // $instance静态变量是Container对象的属性
    static::setInstance($this);
    // 调用instance方法,将容器实例挂载到instances数组的app key上
    $this->instance('app', $this);
    // 同上,挂载到instances数组上,key为Container::class
    $this->instance(Container::class, $this);
    //
    $this->singleton(Mix::class);

    $this->singleton(PackageManifest::class, function () {
        return new PackageManifest(
            new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
        );
    });
}

setInstance()方法

php
public static function setInstance(ContainerContract $container = null)
{
    return static::$instance = $container;
}

查看singleton()方法

singleton()方法

php
public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}

继续查看bind()方法

bind()方法

bind方法是Laravel实现绑定机制的核心方法之一 bind把接口和实现类绑定,当make解析接口的时候创建其实现类的实例对象singleton把接口和其实现类绑定,当第一次make解析的时候创建实例,后面都返回该实例不再创建 instance把接口和其实现类的实例绑定,直接绑定实例对象

php
// 三个参数
// 1.首先明确第一个参数$abstract,简单说就是id,可以当作是存入容器中的名字,它可以使一个字符串,一个类甚至是一个接口。
// 2.第二个参数$concrete简单说就是真实的值,可以当作是一个真正存入容器的实体。他可以是一个实现类,实例或者一个闭包。
// 3.第三个参数控制shared的值
public function bind($abstract, $concrete = null, $shared = false)
{
    // 绑定前先清空instances和aliases中存在同名字的服务
    $this->dropStaleInstances($abstract);

    // 如果没有指定具体的实现类,那么就使用$abstract当作实现类
    if (is_null($concrete)) {
        $concrete = $abstract;
    }

    // 如果$concrete不是一个闭包,那么就判断是否是一个字符串,如果不是,那么就抛出异常
    if (! $concrete instanceof Closure) {
        if (! is_string($concrete)) {
            throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
        }

        /**
         * 以上条件都满足
         * 通过返回创建对象闭包的方式,无需先创建对应的类实例,只需要保存生成对象的闭包
         * 好处
         * 1. 不用事先创建好对象,在需要用到该类的地方才创建。这有助于应用在"准备启动"的阶段节省内存。
         * 2. 能更方便地控制对象的创建和获取,比如resovle方法中实现的各种方式的对象解析:
         *      1、singleton单例(首次创建,之后缓存);2、扩展;3、回调事件触发
         *  */ 
        $concrete = $this->getClosure($abstract, $concrete);
    }

    /**
     * 将返回的闭包和$shared绑定到成员变量$bindings中
     * key 为$abstract,
     * value 为一个数组,包含两个元素,一个是$concrete,一个是$shared
     */ 
    $this->bindings[$abstract] = compact('concrete', 'shared');

    // If the abstract type was already resolved in this container we'll fire the
    // rebound listener so that any objects which have already gotten resolved
    // can have their copy of the object updated via the listener callbacks.
    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}
dropStaleInstances()方法

清空数组$instances$aliases中存在的key

php
protected function dropStaleInstances($abstract)
{
    unset($this->instances[$abstract], $this->aliases[$abstract]);
}
getClosure()方法 (重点)

返回一个闭包,根据传入的参数,调用buildresolve方法,在app方法中,在设置基础目录时,也调用了这个方法

php
protected function getClosure($abstract, $concrete)
{
    return function ($container, $parameters = []) use ($abstract, $concrete) {
        if ($abstract == $concrete) {
            return $container->build($concrete);
        }

        return $container->resolve(
            $concrete, $parameters, $raiseEvents = false
        );
    };
}
build()方法

返回实例对象

php
public function build($concrete)
{
    // 如果$concrete是一个闭包,那么就直接调用该闭包,并将容器实例和参数传入该闭包
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }

    try {
        $reflector = new ReflectionClass($concrete);
    } catch (ReflectionException $e) {
        throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
    }

    // $reflector能否实例化,不能实例化就抛出异常
    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }

    $this->buildStack[] = $concrete;
    // 获取对象的构造函数信息,如果没有定义构造函数,则返回null
    $constructor = $reflector->getConstructor();

    // 如果构造函数方法不存在,则直接实例化对象,并返回
    if (is_null($constructor)) {
        array_pop($this->buildStack);

        return new $concrete;
    }

    // 获取构造函数的参数信息
    $dependencies = $constructor->getParameters();

    // Once we have all the constructor's parameters we can create each of the
    // dependency instances and then use the reflection instances to make a
    // new instance of this class, injecting the created dependencies in.
    try {
        $instances = $this->resolveDependencies($dependencies);
    } catch (BindingResolutionException $e) {
        array_pop($this->buildStack);

        throw $e;
    }

    array_pop($this->buildStack);
    // 使用反射实例化对象
    return $reflector->newInstanceArgs($instances);
}

总结

  1. 首先,将容器绑定到静态成员变量 $instance中,
  2. 容器绑定到成员变量$instances上的app键,
  3. 同理,将容器绑定到成员变量$instances上的Container::class
  4. 注册共享变量绑定到成员变量$bindings上,格式为
    • key 为abstract,value 为一个数组,包含两个元素,一个是$concrete,一个是$shared