Laravel源码剖析之make详解(三)

Laravel的make方法可以说是框架的灵魂所在,在make方法中实现了解耦,依赖注入以及控制反转,咱们现在来一一说明。

首先定位到Illuminate\Foundation\Application的make方法:

   /**
     * Resolve the given type from the container.
     * 从容器中解析被给的类型
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    public function make($abstract, array $parameters = [])
    {
        //先忽略这行
        $this->loadDeferredProviderIfNeeded($abstract = $this->getAlias($abstract));

        return parent::make($abstract, $parameters);
    }

可以看到它调用了父类Illuminate\Container\Container的make方法:

   /**
     * Resolve the given type from the container.
     * 从容器中解析被给的类型
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function make($abstract, array $parameters = [])
    {
        return $this->resolve($abstract, $parameters);
    }

   /**
     * Resolve the given type from the container.
     * 从容器中解析被给的类型
     * @param  string  $abstract
     * @param  array  $parameters
     * @param  bool  $raiseEvents
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
    {
        //获取类的别名
        $abstract = $this->getAlias($abstract);
        
        //从abstractAliases数组中检测有没有这个实例
        $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.
        //如果该类型的实例当前作为单实例进行管理,我们将只返回一个现有实例,而不是实例化
          新实例,这样开发人员每次都可以继续使用相同的对象实例
          也就是说,如果在instances这个数组中已经存在了这个实例,则直接只用此实例,不在从新
          创建新的实例
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }

        $this->with[] = $parameters;
        
        //得到实例,下边有getConcrete方法的说明
        $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.
        // 我们已经准备好实例化为绑定注册的具体类型的实例。这将实例化类型,并递归地解析其
           任何“嵌套”依赖项,直到所有依赖项都得到解析。
        //isBuildable方法下方有说明,判断实例是否是类本身或者实例是一个闭包,返回true或false
        if ($this->isBuildable($concrete, $abstract)) {
            //build方法下方有说明
            $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;
        }

        if ($raiseEvents) {
            $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.
        // 在返回之前,我们还会将resolved标志设置为“true”,并弹出此生成的参数覆盖。完成这两
           件事之后,我们将准备返回完全构造的类实例
        $this->resolved[$abstract] = true;

        array_pop($this->with);

        return $object;
    }

    /**
     * Get the concrete type for a given abstract.
     * 从被给的类得到一个具体的类型,此处会返回一个闭包
     * @param  string  $abstract
     * @return mixed
     */
    protected function getConcrete($abstract)
    {
        if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
            return $concrete;
        }

        // If we don't have a registered resolver or concrete for the type, we'll just
        // assume each type is a concrete name and will attempt to resolve it as is
        // since the container should be able to resolve concretes automatically.
        // 如果我们没有为该类型注册的解析程序或具体名称,我们将假定每个类型都是一个具体名称,
           并将尝试按原样解析它,因为容器应该能够自动解析具体名称
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];
        }

        return $abstract;
    }

    /**
     * Determine if the given concrete is buildable.
     * 确定被给的实例化是可靠的
     * @param  mixed  $concrete
     * @param  string  $abstract
     * @return bool
     */
    protected function isBuildable($concrete, $abstract)
    {
        //判断实例是否是类本身或者实例是一个闭包,返回true或false
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    /**
     * Instantiate a concrete instance of the given type.
     * 实例化给定类型的具体实例,也就是获取具体实例
     * @param  string  $concrete
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    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());
        }

        try {
            //如果不是闭包,说明是类本身,注意$concrete此时只是一个类名,这里利用了 
              ReflectionClass映射这个类
            $reflector = new ReflectionClass($concrete);
        } catch (ReflectionException $e) {
            throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
        }

        // If the type is not instantiable, the developer is attempting to resolve
        // an abstract type such as an Interface or 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.
        // 一旦我们拥有了所有构造函数的参数,我们就可以创建每个依赖项实例,然后使用反射实例
           创建这个类的新实例,将创建的依赖项注入。
        try {
            //递归分析依赖项,下方有说明resolveDependencies方法
            $instances = $this->resolveDependencies($dependencies);
        } catch (BindingResolutionException $e) {
            array_pop($this->buildStack);

            throw $e;
        }

        array_pop($this->buildStack);

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

    /**
     * Resolve all of the dependencies from the ReflectionParameters.
     * 解析映射类的参数的所有依赖项
     * @param  \ReflectionParameter[]  $dependencies
     * @return array
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    protected function resolveDependencies(array $dependencies)
    {
        $results = [];

        foreach ($dependencies as $dependency) {
            // If this dependency has a override for this particular build we will use
            // that instead as the value. Otherwise, we will continue with this run
            // of resolutions and let reflection attempt to determine the result.
            // 如果这个依赖项对这个特定的构建有覆盖,我们将使用它作为值。否则,我们将继续执行
               这一系列决议,并让反射尝试确定结果。
            if ($this->hasParameterOverride($dependency)) {
                $results[] = $this->getParameterOverride($dependency);

                continue;
            }

            // If the class is null, it means the dependency is a string or some other
            // primitive type which we can not resolve since it is not a class and
            // we will just bomb out with an error since we have no-where to go.
            // 如果该类为null,则表示依赖关系是一个字符串或其他一些基本类型,我们无法解析它们
               ,因为它不是一个类,并且我们将由于没有方向而导致错误
            // 这里如果是类的话,会调用resolveClass方法,下方有说明
            $results[] = is_null(Util::getParameterClassName($dependency))
                            ? $this->resolvePrimitive($dependency)
                            : $this->resolveClass($dependency);
        }

        return $results;
    }

    /**
     * Resolve a class based dependency from the container.
     * 从容器中解析一个类的依赖,这里再次调用了make,得到类的实例
     * @param  \ReflectionParameter  $parameter
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    protected function resolveClass(ReflectionParameter $parameter)
    {
        try {
            //这里才真正使用了递归去获取本类以及类所有的依赖的实例
            return $this->make(Util::getParameterClassName($parameter));
        }

        // If we can not resolve the class instance, we will check to see if the value
        // is optional, and if it is we will return the optional parameter value as
        // the value of the dependency, similarly to how we do this with scalars.
        catch (BindingResolutionException $e) {
            if ($parameter->isOptional()) {
                return $parameter->getDefaultValue();
            }

            throw $e;
        }
    }

从上边这一系列方法可以看出,最终ReflectionClass成了解析这个类最关键的一步,然后利用递归分析出这个类的依赖,然后给所有依赖进行实例化,知道把所有需要的东西全部实例化为止。

以上就是对make方法的全部讲解了,第一次完整的阅读Laravel源码,有不足的地方,希望各位大佬指正

下篇咱们开始讲Laravel的请求,由于篇幅较长,一共分为上中下三篇,希望感兴趣的小伙伴可以耐心看完,主要讲解下边这部分代码:

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

感兴趣的小伙伴可以关注一下,你的关注是我最大的动力,也可以私信我一起探讨,大家一起进步,人少可以走的更快,而人多才可以走的更远,咱们一起努力

猜你喜欢

转载自blog.csdn.net/Attitude_do_it/article/details/121512469
今日推荐