声明:本文非博主原创,而是来自《Laravel 4 From Apprentice to Artisan》阅读的翻译理解,当然不是原文翻译,90%真实性可以保证,另外由于是理解翻译,难免有错误,欢迎指正。欢迎转载,转载请注明出处,谢谢!控制反转容器基本绑定在上一章中,我们了解了依赖注入。接下来,我们继续探讨“控制反转”或“依赖反转”。后面我们使用IoC容器就是参考上面的定义。IoC容器使得类的依赖管理变得非常方便,Laravel的核心就是由这种强大的容器思想驱动的。IoC容器是Laravel框架的重要组成部分,它将框架中的所有组件组织起来协同工作。实际上,Laravel的Application类是从Container容器类集成而来的。控制反转容器(IoCContainer)控制反转使得依赖注入更加方便。如果容器中定义了相关的类或接口(契约),我们如何在程序中解析和注入这些对象呢?在Laravel应用中,IoC容器可以使用按照门面设计模式实现的App类来访问容器。容器中包含了多种方法,这里我们只介绍一些基本的方法。下面继续讨论基于上一章的BillerInterface和BillingNotifierInterface使用Stripe1实现的支付功能。我们可以将Stripe的接口实现绑定到容器中,如下:App::bind('BillerInterface',function(){returnnewStripeBiller(App::make('BillingNotifierInterface'));})这里注意,我们在绑定BillerInterface的时候,它还注入了由BillingNotifierInterface接口实现的特定类,因此接口必须绑定到容器:App::bind('BillingNotifierInterface',function(){returnnewEmailBillingNotifier;});如上,我们可以理解为容器就是各种接口对应实现类绑定的地方。一旦将它们绑定到容器,我们就可以在应用程序的任何地方解析和使用它们。我们甚至可以继续将其他内容绑定到解析器中的容器。有瑕疵吗?Laravel控制反转容器是FabienPotencier实现的Pimple2控制反转容器的替代品。如果你的项目中已经使用过Pimple,你可以安心升级到IlluminateContainer3组件,它为你提供了更多好用的功能!一旦使用了容器,切换接口实现就是一件非常简单的事情,简单到一行代码:}}control容器被容器实例化后,包含EmailBillingNotifier的StripeBiller类会随着容器被注入到controller中。现在,如果你想改变通知器的实现,你只需要实现接口绑定:App::bind('BillingNotifierInterface',function(){returnnewSmsBillingNotifier;});现在,您无需担心在项目中的什么地方使用它当涉及到此通知程序时,只需实现新的SmsBillingNotifier类即可。这样,我们的应用程序就可以在不同的场景之间快速切换。是不是觉得这个切换的实现方式很宏大。想象一下,尝试将您的SMS通知程序服务提供商更改为Twilio。我们只需要开发一个使用Twilio通知的实现类,替换容器绑定的接口对应的实现类即可。如果在过渡到Twilio时出现问题,我们可以快速将容器中接口绑定的类替换回原来的服务。在这里,只需要很小的改动就可以快速实现需求的变化。可以看到,依赖注入的优势超乎想象。还有多少例子?好的!然后往下看。有时,我们希望在整个应用程序中只解析和实例化一个类。只需在容器中使用单例方法:App::singleton('BillingNotifierInterface',function(){returnnewSmsBillingNotifier;});现在,容器一单解析订单通知类,在下一个请求中同样会使用一个实例化的实例。容器中的实例方法有点类似于单例;不同的是,你可以传入一个已经存在的对象来更新接口绑定的实例,容器在后续的使用中会使用这个新的对象。$notifier=newSmsBillingNotifier;App::instance('BillingNotifierInterface',$notifier);现在我们已经熟悉了使用容器进行闭包回调的基本方法,下面我们来深入挖掘一下它更强大的功能:反射。容器的独立使用即使不使用Laravel框架,我们仍然可以通过Composer在项目中安装illuminate/container组件来使用Laravel的反转控制容器。反射Laravel容器的一个强大特性是能够通过反射自动解决依赖关系。反射具有检查类及其方法的能力。例如,PHP中的ReflectionClass类允许您检测某些方法在给定类中是否可用。PHP函数method_exists也是一种反射形式。看看下面的代码,让我们来玩一下:$reflection=newReflectionClass('StripeBiller');var_dump($reflection->getMethods());var_dump($reflection->getConstants());有了这个特性,Laravel可以实现一些有趣的功能!例如,下面的代码:classUserControllerextendsBaseController{publicfunction__construct(StripeBiller$biller){$this->biller=$biller;}}上面的controller在初始化的时候,需要传入一个StripeBiller类型的对象,我们可以通过反射来进行类型检测。当Laravel容器没有对应的解析器绑定时,它会尝试通过反射来解析该类。流程大致如下:容器中是否有StripeBiller解析器?没有解析器?映射类StripeBiller确定其依赖项。递归解析StripeBiller类的所有依赖项。通过ReflectionClass->newInstanceArgs()实例化一个新的StripeBiller。可以看到,容器为你做了很多繁重的工作,让你腾出更多的时间来编写各种逻辑代码类。这是Laravel容器独有的强大特性,也是构建大型应用的必杀技。现在,我们的控制器中的代码更改为这样,会发生什么?类UserController扩展BaseController{publicfunction__construct(BillerInterface$biller){$this->biller=$biller;如果我们不绑定BillerInterface,容器如何注入它的依赖类?请注意,接口只是一个约定,它不能被实例化。容器无法在未获得任何信息的情况下实例化依赖项。所以我们需要使用bind方法为接口指定一个默认的类实现:App::bind('BillerInterface','StripBiller');这里我们用一个闭包替换字符串并将其传递到容器中,这将告诉容器任何始终使用实现BillerInterface接口的StripeBiller类。这里,我们只需要修改一行代码就可以替换容器中的绑定逻辑。如果我们想使用余额支付代替现有的支付,只需要完成继承自BillerInterface接口的实现类BalanceInterfacez,修改容器中的绑定:App::bind('BillerInterface','BalancedBiller');application会自动解析并使用这个新的支付方式。同样的,我们也可以使用单例的方式绑定接口,这样容器在整个请求周期内只会被实例化一次。App::singleton('BillerInterface','StripeBiller');掌握容器想了解更多关于Laravel容器的知识?然后通读代码!容器只有一个类文件,IlluminateContainerContainer。看完这个文件的代码,你一定会对容器有更深入、更全面的了解。-2015-04-02二读更正。2015-02-14首次翻译发布。
