Laravel 服务容器源码讲解
Laravel 服务容器是一个用于管理类依赖以及实现依赖注入的强有力工具。通过服务容器对象的 bind 方法将服务类的名称与创建方式,即闭包函数关联在一起,挂载到服务容器中。
容器对象通过反射,解析到有需要该类型对象时,自动创建并注入。bind() 方法是基础方法,其他,单例的绑定 singleton, 带作用域单例绑定 scoped, 绑定实例 instance 都是调用 bind 方法实现的。
绑定完成之后,生成实例时,需要调用 $app->make() 方法,而 app 的 make() 方法调用父级容器的 make() 方法,接着用调的 resolve()方法。
服务提供者,是指引导批量注册服务的一种实现方式。服务提供者有引导方法 boot() 和注册方法 register(), 所有服务提供者的 register() 先执行完,然后再执行 所有的 boot() 方法,以保证 boot() 方法执行时,所需要的服务未注册的情况发生。
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);
// 如果只传一个参数,哪唯一的参数就是 实现函数
if (is_null($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);
}
}
/**
* Resolve the given type from the container.
*
* @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);
}
/**
* Leaning
*
* @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;
}
}