Laravel 源码学习笔记 6:服务容器之 App 解析实例
关于服务容器这块,已经有一个我感觉很棒的文章说明了,深入剖析 Laravel 服务容器
,这篇笔记主要记录一些看代码的收获
我们都知道app(xxxinterface:class)是为了解析这个接口绑定的依赖,也就是这个接口绑定的实现类,那如果是直接一个类呢比如app(xxx:class)有一天我就疑惑了,因为这种没有绑定在容器中,他是如何解析的?我就去看了下源码
在容器类中有make方法
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
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;
}
所有的app解析,都是用的这个方法解析出对象,那如果直接传个类,他是怎么办呢?看上面代码发现有个$concrete = $this->getConcrete($abstract);里面是
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.
// 如果直接用app(axxx::class)这种方法,自定义的,就是直接返回当前这个类了,因为没有在容器中绑定
if (isset($this->bindings[$abstract])) {
return $this->bindings[$abstract]['concrete'];
}
return $abstract;
}
由这个getConcrete方法可以发现,它会去判断容器中有没有存在这个绑定,有的话就会返回它的实现类,没有的话,就会返回它自己。所以我们自定义的类也可以这样解析出对象来调用里面的方法
$xxxClassObject = app(xxx::class);
$xxxClassObject->xxxClassMethod('调用方法');
服务容器:其实就是管理类的依赖和执行依赖注入的东西,里面主要就是存放了接口和实现类的绑定,有的一个请求只需要一个实例那就用单例绑定,普通的就用普通绑定,我们使用依赖注入的时候,就是让容器去解析一个对象出来,如果app(接口)就会去找对应的绑定的实现类,如果app(类)就会返回对应类的实例,其实也就是实现了解耦的过程,少调了new这个东西,也就是控制反转了
容器解析对象就是利用反射
有什么不对或有疑问的地方请大佬们指正:)
本作品采用《CC 协议》,转载必须注明作者和本文链接