热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

依赖注入_php依赖注入容器

篇首语:本文由编程笔记#小编为大家整理,主要介绍了php依赖注入容器相关的知识,希望对你有一定的参考价值。原文: http://blog.csdn.net/r

篇首语:本文由编程笔记#小编为大家整理,主要介绍了php 依赖注入容器相关的知识,希望对你有一定的参考价值。


原文: http://blog.csdn.net/realghost/article/details/35212285

https://my.oschina.net/cxz001/blog/533166

http://www.oschina.net/code/snippet_1171845_48046

http://blog.csdn.net/wzllai/article/details/24485245

看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了,现在确实很懒变得!

首先,我们假设,我们要开发一个组件命名为SomeComponent。这个组件中现在将要注入一个数据库连接。

在这个例子中,数据库连接在component中被创建,这种方法是不切实际的,这样做的话,我们将不能改变数据库连接参数及数据库类型等一些参数。

 




[php] view plain copy
 

 技术分享技术分享



  1. php  

  2.   

  3. class SomeComponent  

  4. {  

  5.   

  6.     /** 

  7.      * The instantiation of the connection is hardcoded inside 

  8.      * the component so is difficult to replace it externally 

  9.      * or change its behavior 

  10.      */  

  11.     public function someDbTask()  

  12.     {  

  13.         $connection = new Connection(array(  

  14.             "host" => "localhost",  

  15.             "username" => "root",  

  16.             "password" => "secret",  

  17.             "dbname" => "invo"  

  18.         ));  

  19.   

  20.         // ...  

  21.     }  

  22.   

  23. }  

  24.   

  25. $some = new SomeComponent();  

  26. $some->someDbTask();  

 

为了解决上面所说的问题,我们需要在使用前创建一个外部连接,并注入到容器中。就目前而言,这看起来是一个很好的解决方案:

 




[php] view plain copy
 

 技术分享技术分享




  1.   

  2. class SomeComponent  

  3. {  

  4.   

  5.     protected $_connection;  

  6.   

  7.     /** 

  8.      * Sets the connection externally 

  9.      */  

  10.     public function setConnection($connection)  

  11.     {  

  12.         $this->_connection = $connection;  

  13.     }  

  14.   

  15.     public function someDbTask()  

  16.     {  

  17.         $connection = $this->_connection;  

  18.   

  19.         // ...  

  20.     }  

  21.   

  22. }  

  23.   

  24. $some = new SomeComponent();  

  25.   

  26. //Create the connection  

  27. $connection = new Connection(array(  

  28.     "host" => "localhost",  

  29.     "username" => "root",  

  30.     "password" => "secret",  

  31.     "dbname" => "invo"  

  32. ));  

  33.   

  34. //Inject the connection in the component  

  35. $some->setConnection($connection);  

  36.   

  37. $some->someDbTask();  


现在我们来考虑一个问题,我们在应用程序中的不同地方使用此组件,将多次创建数据库连接。使用一种类似全局注册表的方式,从这获得一个数据库连接实例,而不是使用一次就创建一次。

 

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. class Registry  

  3. {  

  4.   

  5.     /** 

  6.      * Returns the connection 

  7.      */  

  8.     public static function getConnection()  

  9.     {  

  10.        return new Connection(array(  

  11.             "host" => "localhost",  

  12.             "username" => "root",  

  13.             "password" => "secret",  

  14.             "dbname" => "invo"  

  15.         ));  

  16.     }  

  17.   

  18. }  

  19.   

  20. class SomeComponent  

  21. {  

  22.   

  23.     protected $_connection;  

  24.   

  25.     /** 

  26.      * Sets the connection externally 

  27.      */  

  28.     public function setConnection($connection){  

  29.         $this->_connection = $connection;  

  30.     }  

  31.   

  32.     public function someDbTask()  

  33.     {  

  34.         $connection = $this->_connection;  

  35.   

  36.         // ...  

  37.     }  

  38.   

  39. }  

  40.   

  41. $some = new SomeComponent();  

  42.   

  43. //Pass the connection defined in the registry  

  44. $some->setConnection(Registry::getConnection());  

  45.   

  46. $some->someDbTask();  




现在,让我们来想像一下,我们必须在组件中实现两个方法,首先需要创建一个新的数据库连接,第二个总是获得一个共享连接:

 

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. class Registry  

  3. {  

  4.   

  5.     protected static $_connection;  

  6.   

  7.     /** 

  8.      * Creates a connection 

  9.      */  

  10.     protected static function _createConnection()  

  11.     {  

  12.         return new Connection(array(  

  13.             "host" => "localhost",  

  14.             "username" => "root",  

  15.             "password" => "secret",  

  16.             "dbname" => "invo"  

  17.         ));  

  18.     }  

  19.   

  20.     /** 

  21.      * Creates a connection only once and returns it 

  22.      */  

  23.     public static function getSharedConnection()  

  24.     {  

  25.         if (self::$_cOnnection===null){  

  26.             $connection = self::_createConnection();  

  27.             self::$_connection = $connection;  

  28.         }  

  29.         return self::$_connection;  

  30.     }  

  31.   

  32.     /** 

  33.      * Always returns a new connection 

  34.      */  

  35.     public static function getNewConnection()  

  36.     {  

  37.         return self::_createConnection();  

  38.     }  

  39.   

  40. }  

  41.   

  42. class SomeComponent  

  43. {  

  44.   

  45.     protected $_connection;  

  46.   

  47.     /** 

  48.      * Sets the connection externally 

  49.      */  

  50.     public function setConnection($connection){  

  51.         $this->_connection = $connection;  

  52.     }  

  53.   

  54.     /** 

  55.      * This method always needs the shared connection 

  56.      */  

  57.     public function someDbTask()  

  58.     {  

  59.         $connection = $this->_connection;  

  60.   

  61.         // ...  

  62.     }  

  63.   

  64.     /** 

  65.      * This method always needs a new connection 

  66.      */  

  67.     public function someOtherDbTask($connection)  

  68.     {  

  69.   

  70.     }  

  71.   

  72. }  

  73.   

  74. $some = new SomeComponent();  

  75.   

  76. //This injects the shared connection  

  77. $some->setConnection(Registry::getSharedConnection());  

  78.   

  79. $some->someDbTask();  

  80.   

  81. //Here, we always pass a new connection as parameter  

  82. $some->someOtherDbTask(Registry::getConnection());  




到此为止,我们已经看到了如何使用依赖注入解决我们的问题。不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。但是从长远来看,这种形式的依赖注入也有一些缺点。

 

例如,如果组件中有较多的依赖关系,我们需要创建多个setter方法传递,或创建构造函数进行传递。另外,每次使用组件时,都需要创建依赖组件,使代码维护不太易,我们编写的代码可能像这样:

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. //Create the dependencies or retrieve them from the registry  

  3. $connection = new Connection();  

  4. $session = new Session();  

  5. $fileSystem = new FileSystem();  

  6. $filter = new Filter();  

  7. $selector = new Selector();  

  8.   

  9. //Pass them as constructor parameters  

  10. $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);  

  11.   

  12. // ... or using setters  

  13.   

  14. $some->setConnection($connection);  

  15. $some->setSession($session);  

  16. $some->setFileSystem($fileSystem);  

  17. $some->setFilter($filter);  

  18. $some->setSelector($selector);  




我想,我们不得不在应用程序的许多地方创建这个对象。如果你不需要依赖的组件后,我们又要去代码注入部分移除构造函数中的参数或者是setter方法。为了解决这个问题,我们再次返回去使用一个全局注册表来创建组件。但是,在创建对象之前,它增加了一个新的抽象层:

 

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. class SomeComponent  

  3. {  

  4.   

  5.     // ...  

  6.   

  7.     /** 

  8.      * Define a factory method to create SomeComponent instances injecting its dependencies 

  9.      */  

  10.     public static function factory()  

  11.     {  

  12.   

  13.         $connection = new Connection();  

  14.         $session = new Session();  

  15.         $fileSystem = new FileSystem();  

  16.         $filter = new Filter();  

  17.         $selector = new Selector();  

  18.   

  19.         return new self($connection, $session, $fileSystem, $filter, $selector);  

  20.     }  

  21.   

  22. }  


 

 

这一刻,我们好像回到了问题的开始,我们正在创建组件内部的依赖,我们每次都在修改以及找寻一种解决问题的办法,但这都不是很好的做法。

一种实用和优雅的来解决这些问题,是使用容器的依赖注入,像我们在前面看到的,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖可以使我们的代码耦合度更低,很好的降低了组件的复杂性:

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. class SomeComponent  

  3. {  

  4.   

  5.     protected $_di;  

  6.   

  7.     public function __construct($di)  

  8.     {  

  9.         $this->_di = $di;  

  10.     }  

  11.   

  12.     public function someDbTask()  

  13.     {  

  14.   

  15.         // Get the connection service  

  16.         // Always returns a new connection  

  17.         $connection = $this->_di->get(‘db‘);  

  18.   

  19.     }  

  20.   

  21.     public function someOtherDbTask()  

  22.     {  

  23.   

  24.         // Get a shared connection service,  

  25.         // this will return the same connection everytime  

  26.         $connection = $this->_di->getShared(‘db‘);  

  27.   

  28.         //This method also requires a input filtering service  

  29.         $filter = $this->_db->get(‘filter‘);  

  30.   

  31.     }  

  32.   

  33. }  

  34.   

  35. $di = new Phalcon\DI();  

  36.   

  37. //Register a "db" service in the container  

  38. $di->set(‘db‘, function(){  

  39.     return new Connection(array(  

  40.         "host" => "localhost",  

  41.         "username" => "root",  

  42.         "password" => "secret",  

  43.         "dbname" => "invo"  

  44.     ));  

  45. });  

  46.   

  47. //Register a "filter" service in the container  

  48. $di->set(‘filter‘, function(){  

  49.     return new Filter();  

  50. });  

  51.   

  52. //Register a "session" service in the container  

  53. $di->set(‘session‘, function(){  

  54.     return new Session();  

  55. });  

  56.   

  57. //Pass the service container as unique parameter  

  58. $some = new SomeComponent($di);  

  59.   

  60. $some->someTask();  


 

现在,该组件只有访问某种service的时候才需要它,如果它不需要,它甚至不初始化,以节约资源。该组件是高度解耦。他们的行为,或者说他们的任何其他方面都不会影响到组件本身。
我们的实现办法

Phalcon\DI 是一个实现了服务的依赖注入功能的组件,它本身也是一个容器。

由于Phalcon高度解耦,Phalcon\DI 是框架用来集成其他组件的必不可少的部分,开发人员也可以使用这个组件依赖注入和管理应用程序中不同类文件的实例。

基本上,这个组件实现了 Inversion of Control 模式。基于此,对象不再以构造函数接收参数或者使用setter的方式来实现注入,而是直接请求服务的依赖注入。这就大大降低了整体程序的复杂性,因为只有一个方法用以获得所需要的一个组件的依赖关系。

此外,这种模式增强了代码的可测试性,从而使它不容易出错。
在容器中注册服务¶

框架本身或开发人员都可以注册服务。当一个组件A要求调用组件B(或它的类的一个实例),可以从容器中请求调用组件B,而不是创建组件B的一个实例。

这种工作方式为我们提供了许多优点:

我们可以更换一个组件,从他们本身或者第三方轻松创建。
在组件发布之前,我们可以充分的控制对象的初始化,并对对象进行各种设置。
我们可以使用统一的方式从组件得到一个结构化的全局实例

服务可以通过以下几种方式注入到容器:

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. //Create the Dependency Injector Container  

  3. $di = new Phalcon\DI();  

  4.   

  5. //By its class name  

  6. $di->set("request", ‘Phalcon\Http\Request‘);  

  7.   

  8. //Using an anonymous function, the instance will lazy loaded  

  9. $di->set("request", function(){  

  10.     return new Phalcon\Http\Request();  

  11. });  

  12.   

  13. //Registering directly an instance  

  14. $di->set("request", new Phalcon\Http\Request());  

  15.   

  16. //Using an array definition  

  17. $di->set("request", array(  

  18.     "className" => ‘Phalcon\Http\Request‘  

  19. ));  


 

在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。

容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。

在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。

用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。

Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. //Register a service "db" with a class name and its parameters  

  3. $di->set("db", array(  

  4.     "className" => "Phalcon\Db\Adapter\Pdo\mysql",  

  5.     "parameters" => array(  

  6.           "parameter" => array(  

  7.                "host" => "localhost",  

  8.                "username" => "root",  

  9.                "password" => "secret",  

  10.                "dbname" => "blog"  

  11.           )  

  12.     )  

  13. ));  

  14.   

  15. //Using an anonymous function  

  16. $di->set("db", function(){  

  17.     return new Phalcon\Db\Adapter\Pdo\Mysql(array(  

  18.          "host" => "localhost",  

  19.          "username" => "root",  

  20.          "password" => "secret",  

  21.          "dbname" => "blog"  

  22.     ));  

  23. });  




以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:

 

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. $di->setParameter("db", 0, array(  

  3.     "host" => "localhost",  

  4.     "username" => "root",  

  5.     "password" => "secret"  

  6. ));  


 

从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:

 




[php] view plain copy
 


 技术分享技术分享





  1. $request = $di->get("request");  


 

或者通过下面这种魔术方法的形式调用:

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. $request = $di->getRequest();  


 

Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。

具体的 Phalcon\Http\Request 请求示例:

 




[php] view plain copy
 


 技术分享技术分享





  1.   

  2. $request = $di->getShared("request");  


 

参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:

 




[php] view plain copy
 


 技术分享技术分享





    1.   

    2. $component = $di->get("MyComponent", array("some-parameter", "other"))  



推荐阅读
  • 本博文基于《Amalgamationofproteinsequence,structureandtextualinformationforimprovingprote ... [详细]
  • 本文讨论了在ASP中创建RazorFunctions.cshtml文件时出现的问题,即ASP.global_asax不存在于命名空间ASP中。文章提供了解决该问题的代码示例,并详细解释了代码中涉及的关键概念,如HttpContext、Request和RouteData等。通过阅读本文,读者可以了解如何解决该问题并理解相关的ASP概念。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
  • 一、Struts2是一个基于MVC设计模式的Web应用框架在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2优点1、实现 ... [详细]
  • MVC中的自定义控件
    怎么样创建自定义控 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
author-avatar
陈豪韦小宝_517
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有