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);
}
}