博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
laravel 服务容器实现原理
阅读量:3738 次
发布时间:2019-05-22

本文共 5016 字,大约阅读时间需要 16 分钟。

前言

通过实现laravel 框架功能,以便深入理解laravel框架的先进思想。

什么是服务容器

服务容器是用来管理类依赖与运行依赖注入的工具。Laravel框架中就是使用服务容器来实现 ** 控制反转 ** 和 ** 依赖注入 **。

什么是控制反转(IoC)和依赖注入(DI)

控制反转(IoC) 就是说把创建对象的** 控制权 进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,也就是 Laravel ** 中的容器。

依赖注入(DI)则是帮助容器实现在运行中动态的为对象提供提依赖的资源。

概念容易不太容易让人理解,举个栗子:

//我们构建一个人的类和一个狗的类 class People{    public $dog = null;    public function __construct()    {        $this->dog = new Dog();    }    public function putDog(){        return $this->dog->dogCall();    }}class Dog{    public function dogCall(){        return '汪汪汪';    }}

这个人在遛狗,突然遇到了死对头,他于是放狗咬人

$people = new People();$people->putDog();

在这个操作中,people类要执行putDog()这个方法,需要依赖Dog类,一般我们像上面一样,在people中利用构造函数来添加这个Dog依赖。如果使用控制反转 依赖注入则是这个样子

class People{    public $dog = null;    public function __construct(Dog $Dog)    {        $this->dog = $dog;    }    public function putDog(){        return $this->dog->dogCall();    }}

People类通过构造参数声明自己需要的 依赖类,由容器自动注入。这样就实现了程序的有效解耦,好处在这就不多说了。

Laravel容器依赖注入的实现

实现原理需要了解的知识点:

闭包(匿名函数):

匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数

反射:PHP 5 以上版本具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释

理解了闭包和反射的基本用法我们来看Laravel中是怎么实现容器的,下面代码是我对laravel框架容器部分代码的简化核心版:

lass Container{    /**     *  容器绑定,用来装提供的实例或者 提供实例的回调函数     * @var array     */    public $building = [];    /**     * 注册一个绑定到容器     */    public function bind($abstract, $concrete = null, $shared = false)    {        if(is_null($concrete)){            $concrete = $abstract;        }        if(!$concrete instanceOf Closure){            $concrete = $this->getClosure($abstract, $concrete);        }        $this->building[$abstract] =  compact("concrete", "shared");    }    //注册一个共享的绑定 单例    public function singleton($abstract, $concrete, $shared = true){        $this->bind($abstract, $concrete, $shared);    }    /**     * 默认生成实例的回调闭包     *     * @param $abstract     * @param $concrete     * @return Closure     */    public function getClosure($abstract, $concrete)    {        return function($c) use($abstract, $concrete){            $method = ($abstract == $concrete)? 'build' : 'make';            return $c->$method($concrete);        };    }    /**     * 生成实例      */    public function make($abstract)    {        $concrete = $this->getConcrete($abstract);        if($this->isBuildable($concrete, $abstract)){            $object = $this->build($concrete);        }else{            $object = $this->make($concrete);        }        return $object;    }    /**     * 获取绑定的回调函数     */    public function getConcrete($abstract)    {        if(! isset($this->building[$abstract])){            return $abstract;        }        return $this->building[$abstract]['concrete'];    }    /**     * 判断 是否 可以创建服务实体     */    public function isBuildable($concrete, $abstract)    {        return $concrete === $abstract || $concrete instanceof Closure;    }    /**     * 根据实例具体名称实例具体对象     */    public function build($concrete)    {        if($concrete instanceof Closure){            return $concrete($this);        }        //创建反射对象        $reflector = new ReflectionClass($concrete);        if( ! $reflector->isInstantiable()){            //抛出异常            throw new \Exception('无法实例化');        }        $constructor = $reflector->getConstructor();        if(is_null($constructor)){            return new $concrete;        }        $dependencies = $constructor->getParameters();        $instance = $this->getDependencies($dependencies);        return $reflector->newInstanceArgs($instance);    }    //通过反射解决参数依赖    public function getDependencies(array $dependencies)    {        $results = [];        foreach( $dependencies as $dependency ){            $results[] = is_null($dependency->getClass())                ?$this->resolvedNonClass($dependency)                :$this->resolvedClass($dependency);        }        return $results;    }    //解决一个没有类型提示依赖    public function resolvedNonClass(ReflectionParameter $parameter)    {        if($parameter->isDefaultValueAvailable()){            return $parameter->getDefaultValue();        }        throw new \Exception('出错');    }    //通过容器解决依赖    public function resolvedClass(ReflectionParameter $parameter)    {        return $this->make($parameter->getClass()->name);    }}

容器的工作流程

接着上面遛狗的例子:

//实例化容器类$app =  new Container();//向容器中填充Dog$app->bind('Dog','App\Dog');//填充People$app->bind('People', 'App\People');//通过容器实现依赖注入,完成类的实例化;$people = $app->make('People');//调用方法echo $people->putDog();

上面示例中我们先实例化容器类,然后使用bind()方法 绑定接口和 生成相应的实例的闭包函数。然后使用make() 函数生成实例对象,在make()中会调用 isBuildable($concrete, $abstract) 来判断 给定的服务实体($concrete参数)是否可以创建,可以创建 就会调用 build($concrete) 函数 ,build($concrete) 函数会判断传的参数是 是** 闭包 还是 具体类名 **,如果是闭包则直接运行,如果是具体类名的话,则通过反射获取该类的构造函数所需的依赖,完成实例化。

** 重点理解 下面这几个函数中 反射的用法,应该就很好理解了 **

build($concrete) getDependencies(array $dependencies) resolvedNonClass(ReflectionParameter $parameter) resolvedClass(ReflectionParameter $parameter)

最后

IoC 理解起来是有点难度,可能文中描述让你感觉不是很清楚,可以将文中代码 在php中用debug观察 运行状态。

理解了容器的具体实现原理,再去看Laravel中的相关实现,就会感觉豁然开朗。

原文地址: 

转载地址:http://ettin.baihongyu.com/

你可能感兴趣的文章
二叉树的镜像实现(python版)
查看>>
ptqt5控件了解(三)
查看>>
自学C++(一)
查看>>
51单片机介绍(二)
查看>>
STM32F103 入门篇-5-初识STM32
查看>>
后台框架的frameset
查看>>
Spring Jdbc
查看>>
Spring 事务管理
查看>>
Spring中的Mybatis
查看>>
spring与mybatis的整合
查看>>
Spring Mvc
查看>>
Spring Mvc的核心类与注解
查看>>
json数据交换和RESTful支持
查看>>
spring中的拦截器
查看>>
文件上传和下载
查看>>
Oracle指令,软件架构,
查看>>
Oracle新建用户,授权,单表查询,连接符,去重,排序,逻辑运算,where子查询
查看>>
oracle5:oracle的图形界面操作,分页查询,练习
查看>>
密码学基础之对称密码体制和公钥密码体制
查看>>
Spark Streaming进阶
查看>>