Laravel 之道第六章:服务容器之 make

栏目: PHP · 发布时间: 7年前

内容简介: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 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;

    // ★核心二★:通过 $abstract 到 bindings 属性中取得生成对象的闭包函数,没有则返回 $abstract 本身
    $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.
    // ★核心三★:判断是否可以创建对象,来确定调用 build 方法还是重新调用 make 方法
    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;
}

由于第一次执行,核心一忽略。当以 '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)
{
    // If the concrete type is actually a Closure, we will just execute it and
    // hand back the results of the functions, which allows functions to be
    // used as resolvers for more fine-tuned resolution of these objects.
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }

    $reflector = new ReflectionClass($concrete);

    // If the type is not instantiable, the developer is attempting to resolve
    // an abstract type such as an Interface of Abstract Class and there is
    // no binding registered for the abstractions so we need to bail out.
    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }

    $this->buildStack[] = $concrete;

    $constructor = $reflector->getConstructor();

    // If there are no constructors, that means there are no dependencies then
    // we can just resolve the instances of the objects right away, without
    // resolving any other types or dependencies out of these containers.
    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.
    $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 自动处理,用户可以不再管理那个依赖这个依赖了,不仅变得方便、变得解耦,还变得功能健全

本文章首发在 Laravel China 社区

我们是一群被时空压迫的孩子。 ---- 爱因斯坦

以上所述就是小编给大家介绍的《Laravel 之道第六章:服务容器之 make》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Practical Django Projects, Second Edition

Practical Django Projects, Second Edition

James Bennett / Apress / 2009 / 44.99

Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具