Laravel 服务容器源码讲解

Laravel 服务容器是一个用于管理类依赖以及实现依赖注入的强有力工具。通过服务容器对象的 bind 方法将服务类的名称与创建方式,即闭包函数关联在一起,挂载到服务容器中。

容器对象通过反射,解析到有需要该类型对象时,自动创建并注入。bind() 方法是基础方法,其他,单例的绑定 singleton, 带作用域单例绑定 scoped, 绑定实例 instance 都是调用 bind 方法实现的。

绑定完成之后,生成实例时,需要调用 $app->make() 方法,而 app 的 make() 方法调用父级容器的 make() 方法,接着用调的 resolve()方法。

服务提供者,是指引导批量注册服务的一种实现方式。服务提供者有引导方法 boot() 和注册方法 register(), 所有服务提供者的 register() 先执行完,然后再执行 所有的 boot() 方法,以保证  boot() 方法执行时,所需要的服务未注册的情况发生。

 

<?php

class Container {

    /**
     * 容器绑定
     * @param  string  $abstract 名称
     * @param  \Closure|string|null  $concrete 实现函数
     * @param  bool  $shared 是否共享,即单例
     * @return void
     * @throws \TypeError
     */
    public function bind($abstract, $concrete = null, $shared = false)
    {

        //如果存在的话,直接丢弃旧的实例
        $this->dropStaleInstances($abstract);

        // 如果只传一个参数,比如 $app->bind(Abc::class);
        if (is_null($concrete)) {
            // 实现方法等于抽象名,$this->getClosure($abstract, $concrete); 返回类似于 function(){  return $container->build($concrete); // build($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');
            }

            //返回一个新的闭包函数
            $concrete = $this->getClosure($abstract, $concrete);
        }

        // 放入 $this->bindings 数组,结构类似于:
        // $this->bindings["App\Models\User"] = [ 'concrete'=> Closure, 'shared' => false ]
        // 后面会在 \Illuminate\Container\Container::getConcrete 方法中使用到
        $this->bindings[$abstract] = compact('concrete', 'shared');


        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }



    /**
     * 生成对象
     * @param  string|callable  $abstract
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function make($abstract, array $parameters = [])
    {
        return $this->resolve($abstract, $parameters);
    }


      /**
     * 解析创建对象过程
     * @param  string|callable  $abstract
     * @param  array  $parameters
     * @param  bool  $raiseEvents
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     * @throws \Illuminate\Contracts\Container\CircularDependencyException
     */
    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
    {

        $abstract = $this->getAlias($abstract);

        if ($raiseEvents) {
            $this->fireBeforeResolvingCallbacks($abstract, $parameters);
        }

        $concrete = $this->getContextualConcrete($abstract);

        $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);

        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }

        // 将参数 放入 $this->with 栈
        $this->with[] = $parameters;

        if (is_null($concrete)) {

            //从 $this->bindings[$abstract]['concrete'] 中获取闭包函数
            $concrete = $this->getConcrete($abstract);
        }

        //如果可以绑定: $concrete === $abstract 或  $concrete 是闭包函数
        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 数组
            $this->instances[$abstract] = $object;
        }

        if ($raiseEvents) {
            $this->fireResolvingCallbacks($abstract, $object);
        }

        // 标记为已经创建
        $this->resolved[$abstract] = true;

        // 弹出参数
        array_pop($this->with);

        return $object;
    }



    /**
     * Get the concrete type for a given abstract.
     *
     * @param  string|callable  $abstract
     * @return mixed
     */
    protected function getConcrete($abstract)
    {

        if (isset($this->bindings[$abstract])) {
            // 从绑定数组中拿闭包函数
            return $this->bindings[$abstract]['concrete'];
        }

        return $abstract;
    }


    /**
     * 改造原有闭包,生成类型时要使用的闭包
     * @param  string  $abstract
     * @param  string  $concrete
     * @return \Closure
     */
    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
            );
        };
    }



    /**
     *
     * 建造对象
     * 这个就是最底层真正创建对象了
     * @param  \Closure|string  $concrete 闭包或者类名
     * @return mixed
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     * @throws \Illuminate\Contracts\Container\CircularDependencyException
     */
    public function build($concrete)
    {

        // 如果是闭包则直接直接调用,第一个参数是容器对象,$this->getLastParameterOverride 会用到 $this->with 保存的参数栈
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }

        try {

            //一般闭包函数内部会调用 build() 或更上层的 resolve() 方法,最终走到这里来创建对象。参见:getClosure() 方法

            $reflector = new ReflectionClass($concrete); // 反射类得到反射对象

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

        // 判断对象能否实例化
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);
        }

        // 加入创建队列
        $this->buildStack[] = $concrete;

        // 获取构造方法对象
        $constructor = $reflector->getConstructor();


        if (is_null($constructor)) {
            array_pop($this->buildStack);
            return new $concrete;
        }

        // 构造方法对象获取参数列表
        $dependencies = $constructor->getParameters();

        try {

            // 解析依赖实际出参数
            $instances = $this->resolveDependencies($dependencies);


        } catch (BindingResolutionException $e) {
            array_pop($this->buildStack);
            throw $e;
        }

        array_pop($this->buildStack);

        // 通过反射对象生成实例对象
        return $reflector->newInstanceArgs($instances);
    }


}

 

posted @ 2023-07-05 00:34  心随所遇  阅读(58)  评论(0编辑  收藏  举报