Laravel 服务容器之 make

未匹配的标注

简介

本篇主要讲解 Laravel 服务容器中 make 方法主要作用

正文

下面是 public/index.php 入口文件的一段代码,相信大家都见过

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

它作用解析出在服务容器 bindings 属性中以 'Illuminate\Contracts\Http\Kernel' 为键,对应的服务对象。据上一章我们知道是 'App\Http\Kernel' 的类对象

注意:以 'Illuminate\Contracts\Http\Kernel' 为键,实际对应不是 'App\Http\Kernel' 的类对象,而是对应能够生成 'App\Http\Kernel' 类对象的闭包函数。

那么他是如何解析出的呢,我们看一下源码

public function make($abstract, array $parameters = [])
{
    return $this->resolve($abstract, $parameters);
}

实际调用了 resolve 方法,我们看一下 resolve 方法。

看下面这方法,是不是头大,这么多,其实核心的就那么几个地方,我已标注

protected function resolve($abstract, $parameters = [])
{
    $abstract = $this->getAlias($abstract);

    $needsContextualBuild = ! empty($parameters) || ! is_null(
        $this->getContextualConcrete($abstract)
    );

    // ★核心一★:检测之前是否解析过对应的对象,有则返回对应对象;一种缓存机制,防止重复解析,提高程序运行效率
    if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
        return $this->instances[$abstract];
    }

    $this->with[] = $parameters;

    // ★核心二★:通过 $abstract 到 bindings 属性中取得生成对象的闭包函数,没有则返回 $abstract 本身
    $concrete = $this->getConcrete($abstract);

    // ★核心三★:判断是否可以创建对象,来确定调用 build 方法还是重新调用 make 方法
    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);

    return $object;
}

由于第一次执行,核心一忽略。当以 'Illuminate\Contracts\Http\Kernel' 为 $abstract 时,获取的闭包函数定义的位置如下

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

        return $container->make($concrete, $parameters);
    };
}

会执行上面代码 function 里面的内容,因为 Illuminate\Contracts\Http\Kernel 不等于 App\Http\Kernel ,故以 App\Http\Kernel 为 $abstract 重新调用了 make 方法

此时的 make 方法可以执行到 build 方法,我们来看一下

public function build($concrete)
{
    // 首先判断 concrete 是不是闭包,如果是就会执行
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }

    // PHP 反射类操作
    $reflector = new ReflectionClass($concrete);

    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }

    $this->buildStack[] = $concrete;

    // 获取 concrete 类的构造方法
    $constructor = $reflector->getConstructor();

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

        return new $concrete;
    }

    // 获取 concrete 类的构造方法上的参数
    $dependencies = $constructor->getParameters();

    // 根据参数的类型约束,注入依赖
    $instances = $this->resolveDependencies(
        $dependencies
    );

    array_pop($this->buildStack);

    return $reflector->newInstanceArgs($instances);
}

此时的 $concrete 等于 'App\Http\Kernel',会调用 PHP 内置的反射类,对 App\Http\Kernel 进行处理。

关于 ReflectionClass

官方文档

ReflectionClass:我的理解是,这是一个对类进行处理的对象,它甚至能够获取类中的注释,执行类中的私有方法;getConstructor 返回一个 ReflectionMethod 对象,它可以对类中方法进行更进一步处理和控制,比如获取方法调用时的参数列表,包含参数的类型约束,这点很重要,因为它是 Laravel 实现依赖注入的手段;反射方法对象中的 getParameters 将返回一个 ReflectionParameter 对象,它里面就包含了类型约束和参数名称。

$instances = $this->resolveDependencies(
    $dependencies
);

看到上面这段代码了吗,resolveDependencies 方法,就是解析出要类构造方法中的约束类,通过多次 make 自动获取所依赖的对象,然后通过反射类的 newInstanceArgs 实例化

最终获取了 App\Http\Kernel 类的对象,并且仅仅通过一行字符串,其中构造函数所依赖的对象,全部由 make 自动处理,用户可以不再管理那个依赖这个依赖了,不仅变得方便、变得解耦,还变得功能健全

本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~