laravel启动分析(一) 依赖注入

289 阅读2分钟

Laravel依赖注入

Laravel容器是用于管理类依赖和注入类依赖的一个容器,所运用的思想是依赖注入和控制翻转,下面先来看一下依赖注入的简单例子

依赖注入的概念详细介绍 blog.csdn.net/sinat_21843…

class Ioc {
    protected $ioc;

    public function __construct(IocResponse $ioc)
    {
        $this->ioc = $ioc;
    }
    //执行ioc容器的方法
    public function get(){
        return $this->ioc->get();
    }
}

使用了依赖注入之后,我们将类的实例化进行解耦

下面我们来看看如何非常简单的获取到构造方法中的类实例化,这里我们需要使用到PHP的反射

//A类
class A
{
    public function __construct()
    {
    }

    public function show(){
        echo "AAA";
    }
}

//B类
class B
{
    public function __construct(A $a)
    {
    }

    public function show() {
        echo "BBB";
    }
}


//C类
class C
{
    protected $a;
    protected $b;
    public function __construct(A $a, B $b)
    {
        $this->a = $a;
        $this->b = $b;

    }
    public function show(){
        $this->a->show();
        $this->b->show();
    }

}

//获取类的实例
function make($name)
{
    //使用类的反射获取到类的实例
    $ref = new ReflectionClass($name);
    //获取到b的构造函数
    $cons = $ref->getConstructor();
    //获取构造函数中的参数
    $arguments = $cons->getParameters();

    //获取实例类
    $dep = dependencies($arguments);

    //进行实例化
    return $ref->newInstanceArgs($dep);
}

//解析类的参数
function dependencies($arg)
{
    $make = [];
    foreach ($arg as $k => $param) {
        //获取它的类
        $dep = $param->getClass();
        //如果不是空的话
        if (!is_null($dep)) {
            //会存在多层的解析,例如上面反射C 得到A B ,B 需要A
            $make [] = make($param->getClass()->name);
        }else{
            //如果是空的话 存在默认值的话 则放入数组中
            if($param->isDefaultValueAvailable()) {
                $make[] = $param->getDefaultValue();
            }
        }
    }
    return $make;
}

$c = make("C");
$c->show();

通过以上简单的反射,我们粗略的实现了类的依赖注入,接下来我们看看Larave是如何实现依赖注入的

//Laravel的依赖注入核心代码

//laravel/framework/src/Illuminate/Container/Container.php
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 of 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);
}


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.
        //如果不是类的话,resolvePrimitive处理
        $results[] = is_null($dependency->getClass())
                        ? $this->resolvePrimitive($dependency)
                        : $this->resolveClass($dependency);
    }

    return $results;
}

protected function resolveClass(ReflectionParameter $parameter)
{
    //再次进行处理
    try {
        return $this->make($parameter->getClass()->name);
    }

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

以上我们对Laravel的依赖注入的核心代码进行了注释说明,可以发现其实跟我们自己的思想大致相同,都是使用PHP的反射,获取到类的属性等