laravel框架在入口index.php文件中通过require实例化Illuminate\Foundation\Application类,用作连接框架各种服务、组件的“胶水”和实现 IoC控制反转的容器。
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
在vendor目录下找到laravel包,laravel核心代码也是一个composer包。找到Illuminate\Foundation\Application类的定义文件.
Application类继承了服务容器Container类,和接口ApplicationContract、HttpKernelInterface。现在假设有三个类,A,B,C。B的实现依赖于A,C的实现又依赖于B,用传统的设计模式,在每次实例化C的时候,都要先把A、B类的定义文件require进来,再实例化A和B,再把这两个对象注入到的C的实例中。这显然是极为繁琐的一件事,假如各个类之间的依赖关系再复杂点,那么就要耗费大量的精力去处理各种依赖。控制反转(Inversion of Control,英文缩写为IoC)是一种新的设计模式,实现IoC后,每次实例化类C的时候,自动注入所依赖的类A和类B。这里的Container类就是做的这样一件事情,从容器中获取C类的对象,容器依次实例化依赖A,B,并注入C中,最后把已经注入依赖实例化后的C类对象返回给我们。在目录\vendor\laravel\framework\src\Illuminate\Container下找到Container容器类的定义文件:
Container容器类,实现了两个接口,ArrayAccess接口和ContainerContract接口,ArrayAccess接口是php的预定义接口,实现这个接口可以像访问数组一样访问对象。实现这个接口需要实现四个方法:http://php.net/manual/zh/class.arrayaccess.php
接下来重点看下ContainerContract接口的实现。Container类定义了很多protected属性:
protected static $instance; 当前存在的容器实例。
protected $resolved = [];保存已经解析的类
protected $bindings = [];绑定到容器的闭包、对象
protected $methodBindings = [];绑定到容器的方法
protected $instances = [];容器中的共享实例
protected $aliases = [];保存类的别名
protected $abstractAliases = [];类的别名到类名的映射
protected $extenders = []; service服务类的扩展
protected $tags = [];标签
protected $buildStack = [];当前实例化过程中保存对象的栈
protected $with = [];实例化时用到的参数
public $contextual = [];上下文环境
protected $reboundCallbacks = [];重新绑定时需要调用的回掉函数
protected $globalResolvingCallbacks = [];所有类解析时要调用的回掉函数
protected $globalAfterResolvingCallbacks = [];所有类解析完成后要调用的回掉函数
protected $resolvingCallbacks = [];某一个类解析时要调用的回掉函数
protected $afterResolvingCallbacks = [];某一个类解析完成后要调用的回掉函数
Container类中的方法:
public function when($concrete) 定义一个上下文绑定。
public function bound($abstract)判断当前类名或别名是否已经绑定
public function resolved($abstract)判断当前类名或别名是否已经解析
public function isShared($abstract)判断是否共享实例
public function isAlias($name)判断是否是别名
public function bind($abstract, $concrete = null, $shared = false)注册绑定
protected function getClosure($abstract, $concrete)获取一个闭包,在解析的时候调用
.......
从容器获取一个实例化一个类,只需要调用make方法:
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
make方法有两个参数,字符串参数$abstract和数组参数$parameters,第一个参数是类名或类的别名,第二个参数是实例化类时需要传入的参数。可以看到make方法实际上是对resolve方法的调用:
/**
* 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;
}
首先获取类别名,再判断是否需要根据上下文环境构建对象,如果是共享实例且不需要根据上下文环境创建,则直接从共享实例数组$this->instances返回对象实例。将$parameters保存到$this->with栈中,调用getConcrete方法获取类的实现$concrete,判断$concrete是否能构建对象。若能则调用build方法,在build的过程中会把类需要的依赖全部解析并注入到最终需要的实例中。
/**
* 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 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.
$instances = $this->resolveDependencies(
$dependencies
);
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
若$concrete是一个闭包,则直接调用闭包,若不是闭包则需要借助php的反射类ReflectionClass来解析依赖,通过对__construct构造方法的参数解析,来注入对象需要的依赖。有关反射类的详细说明,见php官方文档:http://php.net/manual/zh/book.reflection.php。解析完依赖后,调用反射器的newInstanceArgs方法,并传入解析好的依赖,实例化需要的类,这样整个IoC就完成了。