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