摘要:在开始之前要明确一个概念不管是设计模式还是依赖注入等等都是为了实现模块化所谓模块化就是希望一个软件是由很多子模块组成的这些模块之间的依赖程度尽量的低也就是如果系统中不需要某一个功能那么只要移除这个功能所对应的模块就可以了那么我们今天要说的服
在开始之前要明确一个概念,不管是设计模式,还是依赖注入等等,都是为了实现模块化.所谓模块化就是希望一个软件是由很多子模块组成的,这些模块之间的依赖程度尽量的低,也就是如果系统中不需要某一个功能,那么只要移除这个功能所对应的模块就可以了.
那么,我们今天要说的服务容器就是为了实现上面的功能.你应该听过,Laravel中的服务容器其本质上是一个IoC容器,但是好像队IoC又不是很了解,讲来讲去优点很多,功能很强劲.但是不懂原理怎么用都不踏实啊.所以,这里我们自己来实现一个IoC容器,洞察其本质.
在开始之前,先说明一点,阅读本篇文章至少要保证有一下的基础知识:
php反射用法
闭包的use用法
如果不懂上面的内容,请先补充.避免阅读代码时候产生的不适感.
getClosure($abstract, $concrete); } $this->binding[$abstract] = compact("concrete", "shared"); } protected function getClosure($abstract, $concrete) { return function ($c) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? "build" : "make"; return $c->$method($concrete); }; } /** * @param $abstract * @return object * */ public function make($abstract) { $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } return $object; } /** * @param $concrete * @param $abstract * @return bool */ public function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } /** * @param $abstract * @return mixed */ protected function getConcrete($abstract) { if (!isset($this->binding[$abstract])) { return $abstract; } return $this->binding[$abstract]["concrete"]; } /** * @param $concrete * @return object */ public function build($concrete) { if($concrete instanceof Closure) { return $concrete($this); } //反射... $reflector = new ReflectionClass($concrete); if(!$reflector->isInstantiable()) { echo $message = "Target [$concrete] is not instantiable"; } //获取要实例化对象的构造函数 $constructor = $reflector->getConstructor(); //没有定义构造函数,只有默认的构造函数,说明构造函数参数个数为空 if(is_null($constructor)) { return new $concrete; } //获取构造函数所需要的所有参数 $dependencies = $constructor->getParameters(); $instances = $this->getDependencies($dependencies); //从给出的数组参数在中实例化对象 return $reflector->newInstanceArgs($instances); } /** * @param $paramters * @return array * 获取构建类所需要的所有依赖,级构造函数所需要的参数 , */ protected function getDependencies($paramters) { $dependencies = []; foreach ($paramters as $paramter) { //获取到参数名称. $dep = $paramter->getClass(); if(is_null($dep)){ $dependencies = null; }else{ $dependencies[] = $this->resolveClass($paramter); } } return (array)$dependencies; } /** * @param ReflectionParameter $parameter * @return object * 实例化 构造函数中所需要的参数. */ protected function resolveClass(ReflectionParameter $parameter) { $name = $parameter->getClass()->name; return $this->make($name); } }
这就是一个IoC容器的实现代码.乍一看,很麻烦.其实真的蛮麻烦的 =_=,如果是第一次接触的话,并不是那么好消化,这里再给出使用IoC容器的代码
_trafficTool = $trafficTool; } public function visitTibet() { $this->_trafficTool->go(); } } //实例化IoC容器 $app = new Container(); //绑定某一功能到IoC $app->bind("TrafficTool", "Train"); $app->bind("travellerA", "Traveller"); // 实例化对象 $tra = $app->make("travellerA"); $tra->visitTibet();
运行例子发现会输出:train...这个例子假设旅行者去青藏旅行,可以坐火车(train)或者走路(leg)去青藏.
好了,其实这样子本篇文章就可以结束了,因为所有的答案都在IoC容器的实现中, 但是为了可以更好的理解上面的代码,我们继续往下分析.
首先,希望你可以运行一下上面的代码,虽然简单的运行代码并不会帮助你理解代码,但是一个可以运行的例子会让人比较踏实,能够更有把握的理解代码.
在深入每一行代码之前,我们从整体上来分析,IoC解决了一个什么问题?简单点说,就是我们再实例化对象的时候不用使用new了,有了IoC容器之后,我们调用make函数就可以实例化出一个对象了.然而,你发现,Traveller的构造函数是需要一个参数的,可是我们好像并没有提供这个参数?
这就是IoC强大之处了, 调用make实例化对象的时候,容器会使用反射功能,去分析我们要实例化对象的构造函数,获取构造函数所需的每个参数,然后分别去实例化这些参数,如果实例化这些参数也要参数,那么就再去实例化参数的参数.....=_=.到最后成功实例化我们所需要的traveller了.在Container的build函数就是使用反射来实例化对象.
但是,有一个问题了,IoC容器怎么知道实例化Traveller的时候需要的参数train,而不是leg?
其实,IoC容器什么都不知道,IoC会实例化哪些对象都是通过bind函数告诉IoC的,上面的例子两次调用bind函数,就是告诉Ioc可以实例化的对象有Train和Traveller. 再通俗讲就是:当需要当我们需要TrafficTool这个服务的时候去实例化Train这个类,需要一个travellerA的旅行者的时候去实例化Traveller类.而Train这个就是travellerA就是去青藏的方式. 这样子如果想要走路去青藏的话只要把$app->bind("Visit", "Train");改为$app->bind("Visit", "Leg");就可以.
可是,这上面的这些有什么意义?直接$tra = new Traveller($trafficTool)来实例化对象好像也没有什么不好的.
使用new来实例化对象的时候,会产生依赖.比如上面$tra = new Traveller($trafficTool),这说明我们要创建一个Traveller之前得有一个$trafficTool,即Traveller依赖于trafficTool.当使用new来实例化Traveller的时候,Traveller和trafficTool之间就产生了耦合.这样,这两个组件就没办法分开了.
而使用IoC是怎么解决这个问题的,之前说过,如果想要如果想要走路去青藏的话只要把$app->bind("Visit", "Train");改为$app->bind("Visit", "Leg");就可以.这样子,使用何种方式去青藏,我们可以自由的选择.
我们站在Laravel框架设计者的角度去想,设计者肯定希望一个框架提供的功能越多越好,但是又要保证强大的同时又不会限制使用者.最好可以保证使用者想实现什么奇怪的需求都可以.那么功能强大但是又不局限的最好方法就是什么都不做,提供一个强大的IoC容器.所有需要实现的功能都变成一个个服务,需要什么服务就把服务注册(即调用bind函数)到IoC中,然后让IoC去管理依赖.
开发者想到一个{{BANNED}}的需求:走路去青藏,那么只要你实现了走路去青藏这个功能,然后把这个功能当做一个服务注册到IoC中,以后你需要这个服务的时候IoC就帮你实例化这个服务.当开发者回归正常之后觉得还是坐火车去吧,于是不注册走路这个功能,实现坐火车的功能,然后注册这个功能.下次IoC实例化的时候就是实例化坐火车这个功能了.
好了,剩下的部分就是一行一行的阅读Container的代码了,Laravel框架中的服务容器代码也是这个样子,只是功能更加强悍.但是核心是一样的,上面的代码懂了以后再使用Laravel框架就会更加游刃有余了.
文章虽短.但是内容很多.尤其是代码,虽然可能只是短短的一个例子,但是包含了很多内容.值得好好分析,这里放个彩蛋:Traveller中构造函数参数类似为TrafficTool,是一个接口.但是实例化的是Train.这里体现了设计模式的一个原则
面对接口编程,而不是面对实现编程.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/22140.html
摘要:划下重点,服务容器是用于管理类的依赖和执行依赖注入的工具。类的实例化及其依赖的注入,完全由服务容器自动的去完成。 本文首发于 深入剖析 Laravel 服务容器,转载请注明出处。喜欢的朋友不要吝啬你们的赞同,谢谢。 之前在 深度挖掘 Laravel 生命周期 一文中,我们有去探究 Laravel 究竟是如何接收 HTTP 请求,又是如何生成响应并最终呈现给用户的工作原理。 本章将带领大...
摘要:一个服务提供器必须包含至少一种方法。服务提供器一旦被注册,就可被用于程序的各个地方。注意服务提供器的变量来自类中。启动服务当所有的服务提供器注册之后,他们就变成了已启动状态。再次提示,把服务提供器作为一种组织工具来使用。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%...
摘要:服务提供者启动原理之前我们有学习深度挖掘生命周期和深入剖析服务容器,今天我们将学习服务提供者。的所有核心服务都是通过服务提供者进行引导启动的,所以想深入了解那么研究服务提供者的原理是个绕不开的话题。 本文首发于 深入剖析 Laravel 服务提供者实现原理,转载请注明出处。 今天我们将学习 Laravel 框架另外一个核心内容「服务提供者(Service Provider)」。服务提供...
摘要:控制反转容器控制反转使依赖注入变得更加便捷。有瑕疵控制反转容器是实现的控制翻转容器的一种替代方案。容器的独立使用即使没有使用框架,我们仍然可以在项目中使用安装组件来使用的控制反转容器。在没有给定任何信息的情况下,容器是无法实例化相关依赖的。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味...
摘要:组件扩展通常有两种方法向容器中绑定自己的接口实现痛过使用工厂模式实现的类注册自己的扩展。类库管理类以工厂模式实现,负责诸如缓存等驱动的实例化。闭包须要传入继承自和容器的实例化对象。当完成扩展之后要记住中替换成自己的扩展名称。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证9...
阅读 2470·2023-04-25 21:41
阅读 1647·2021-09-22 15:17
阅读 1921·2021-09-22 10:02
阅读 2433·2021-09-10 11:21
阅读 2569·2019-08-30 15:53
阅读 996·2019-08-30 15:44
阅读 946·2019-08-30 13:46
阅读 1125·2019-08-29 18:36