laravel 简单聊聊singleton的实现过程

文章目录

      • 场景
      • 解析
      • 源码

场景

  • 最近在看laravel request lifecycle;laravel 在完成Illuminate\Foundation\Application的实例化之后,接着就注册了三个Kernel的singleton ; 那么singleton 完成了什么操作了呢?
  • 下面就以$app->singleton(Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);为例聊聊实现的过程
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

解析

  • singleton 本质调用的是 Illuminate\Container\Container的bind function

    • $concrete = $this->getClosure($abstract, $concrete); 将$concrete 转换成 Closure function , 里面的封装很重要,return $container->make($concrete, $parameters);
    • $this->bindings[$abstract] = compact('concrete', 'shared'); 将$concrete 和$share 填写到应用的bindings属性上,key 是$abstract
  • 从服务容器中解析的会发生什么呢?

  • 本质上触发的是Illuminate\Container\Container的resolve function

  • if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {return $this->instances[$abstract]; } 除了第一次之后的resolve才会从instances中返回实例

  • $concrete = $this->getConcrete($abstract); 这里获取到的就是上面填充应用的bindings属性, return $this->bindings[$abstract]['concrete'];

  • $object = $this->build($concrete); 这里面的执行非常有意思,

    • 因为$concrete是Closure 所以会执行到这里 c o n c r e t e ( concrete( concrete(this, $this->getLastParameterOverride());
    • 因为这个Closure function函数内部封装的是return $container->make($concrete, $parameters); 不过这次的参数 $container->make(‘App\Http\Kernel::class’, []); 再次触发resove函数,then build(‘App\Http\Kernel::class’)
    • 因为 $concrete === “App\Http\Kernel” $reflector = new ReflectionClass($concrete); return $reflector->newInstanceArgs($instances);
    • 综上得到的是 App\Http\Kernel 实例
  • if ($this->isShared($abstract) && ! $needsContextualBuild) {$this->instances[$abstract] = $object;}
    因为singleton所以实例会存储起来 下次直接从instances属性中获取实例

  • $this->resolved[$abstract] = true; 设置 $abstract resolved过了

  • 返回 App\Http\Kernel::class 实例

源码

    /**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    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;

        $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.
        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;
    }

    /**
     * Determine if the given concrete is buildable.
     *
     * @param  mixed   $concrete
     * @param  string  $abstract
     * @return bool
     */
    protected function isBuildable($concrete, $abstract)
    {
        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());
        }
        
        $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);
    }

你可能感兴趣的:(laravel,singleton,laravel)