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. class SomeComponent
  3. {
  4. /**
  5. * The instantiation of the connection is hardcoded inside
  6. * the component so is difficult to replace it externally
  7. * or change its behavior
  8. */
  9. public function someDbTask()
  10. {
  11. $connection = new Connection(array(
  12. "host" => "localhost",
  13. "username" => "root",
  14. "password" => "secret",
  15. "dbname" => "invo"
  16. ));
  17. // ...
  18. }
  19. }
  20. $some = new SomeComponent();
  21. $some->someDbTask();

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

[php] view plain copy

  1. <?php
  2. class SomeComponent
  3. {
  4. protected $_connection;
  5. /**
  6. * Sets the connection externally
  7. */
  8. public function setConnection($connection)
  9. {
  10. $this->_connection = $connection;
  11. }
  12. public function someDbTask()
  13. {
  14. $connection = $this->_connection;
  15. // ...
  16. }
  17. }
  18. $some = new SomeComponent();
  19. //Create the connection
  20. $connection = new Connection(array(
  21. "host" => "localhost",
  22. "username" => "root",
  23. "password" => "secret",
  24. "dbname" => "invo"
  25. ));
  26. //Inject the connection in the component
  27. $some->setConnection($connection);
  28. $some->someDbTask();

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

[php] view plain copy

  1. <?php
  2. class Registry
  3. {
  4. /**
  5. * Returns the connection
  6. */
  7. public static function getConnection()
  8. {
  9. return new Connection(array(
  10. "host" => "localhost",
  11. "username" => "root",
  12. "password" => "secret",
  13. "dbname" => "invo"
  14. ));
  15. }
  16. }
  17. class SomeComponent
  18. {
  19. protected $_connection;
  20. /**
  21. * Sets the connection externally
  22. */
  23. public function setConnection($connection){
  24. $this->_connection = $connection;
  25. }
  26. public function someDbTask()
  27. {
  28. $connection = $this->_connection;
  29. // ...
  30. }
  31. }
  32. $some = new SomeComponent();
  33. //Pass the connection defined in the registry
  34. $some->setConnection(Registry::getConnection());
  35. $some->someDbTask();

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

[php] view plain copy

  1. <?php
  2. class Registry
  3. {
  4. protected static $_connection;
  5. /**
  6. * Creates a connection
  7. */
  8. protected static function _createConnection()
  9. {
  10. return new Connection(array(
  11. "host" => "localhost",
  12. "username" => "root",
  13. "password" => "secret",
  14. "dbname" => "invo"
  15. ));
  16. }
  17. /**
  18. * Creates a connection only once and returns it
  19. */
  20. public static function getSharedConnection()
  21. {
  22. if (self::$_connection===null){
  23. $connection = self::_createConnection();
  24. self::$_connection = $connection;
  25. }
  26. return self::$_connection;
  27. }
  28. /**
  29. * Always returns a new connection
  30. */
  31. public static function getNewConnection()
  32. {
  33. return self::_createConnection();
  34. }
  35. }
  36. class SomeComponent
  37. {
  38. protected $_connection;
  39. /**
  40. * Sets the connection externally
  41. */
  42. public function setConnection($connection){
  43. $this->_connection = $connection;
  44. }
  45. /**
  46. * This method always needs the shared connection
  47. */
  48. public function someDbTask()
  49. {
  50. $connection = $this->_connection;
  51. // ...
  52. }
  53. /**
  54. * This method always needs a new connection
  55. */
  56. public function someOtherDbTask($connection)
  57. {
  58. }
  59. }
  60. $some = new SomeComponent();
  61. //This injects the shared connection
  62. $some->setConnection(Registry::getSharedConnection());
  63. $some->someDbTask();
  64. //Here, we always pass a new connection as parameter
  65. $some->someOtherDbTask(Registry::getConnection());

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

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

[php] view plain copy

  1. <?php
  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. //Pass them as constructor parameters
  9. $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);
  10. // ... or using setters
  11. $some->setConnection($connection);
  12. $some->setSession($session);
  13. $some->setFileSystem($fileSystem);
  14. $some->setFilter($filter);
  15. $some->setSelector($selector);

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

[php] view plain copy

  1. <?php
  2. class SomeComponent
  3. {
  4. // ...
  5. /**
  6. * Define a factory method to create SomeComponent instances injecting its dependencies
  7. */
  8. public static function factory()
  9. {
  10. $connection = new Connection();
  11. $session = new Session();
  12. $fileSystem = new FileSystem();
  13. $filter = new Filter();
  14. $selector = new Selector();
  15. return new self($connection, $session, $fileSystem, $filter, $selector);
  16. }
  17. }

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

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

[php] view plain copy

  1. <?php
  2. class SomeComponent
  3. {
  4. protected $_di;
  5. public function __construct($di)
  6. {
  7. $this->_di = $di;
  8. }
  9. public function someDbTask()
  10. {
  11. // Get the connection service
  12. // Always returns a new connection
  13. $connection = $this->_di->get(‘db‘);
  14. }
  15. public function someOtherDbTask()
  16. {
  17. // Get a shared connection service,
  18. // this will return the same connection everytime
  19. $connection = $this->_di->getShared(‘db‘);
  20. //This method also requires a input filtering service
  21. $filter = $this->_db->get(‘filter‘);
  22. }
  23. }
  24. $di = new Phalcon\DI();
  25. //Register a "db" service in the container
  26. $di->set(‘db‘, function(){
  27. return new Connection(array(
  28. "host" => "localhost",
  29. "username" => "root",
  30. "password" => "secret",
  31. "dbname" => "invo"
  32. ));
  33. });
  34. //Register a "filter" service in the container
  35. $di->set(‘filter‘, function(){
  36. return new Filter();
  37. });
  38. //Register a "session" service in the container
  39. $di->set(‘session‘, function(){
  40. return new Session();
  41. });
  42. //Pass the service container as unique parameter
  43. $some = new SomeComponent($di);
  44. $some->someTask();

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

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

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

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

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

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

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

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

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

[php] view plain copy

  1. <?php
  2. //Create the Dependency Injector Container
  3. $di = new Phalcon\DI();
  4. //By its class name
  5. $di->set("request", ‘Phalcon\Http\Request‘);
  6. //Using an anonymous function, the instance will lazy loaded
  7. $di->set("request", function(){
  8. return new Phalcon\Http\Request();
  9. });
  10. //Registering directly an instance
  11. $di->set("request", new Phalcon\Http\Request());
  12. //Using an array definition
  13. $di->set("request", array(
  14. "className" => ‘Phalcon\Http\Request‘
  15. ));

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

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

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

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

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

[php] view plain copy

  1. <?php
  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. //Using an anonymous function
  15. $di->set("db", function(){
  16. return new Phalcon\Db\Adapter\Pdo\Mysql(array(
  17. "host" => "localhost",
  18. "username" => "root",
  19. "password" => "secret",
  20. "dbname" => "blog"
  21. ));
  22. });

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

[php] view plain copy

  1. <?php
  2. $di->setParameter("db", 0, array(
  3. "host" => "localhost",
  4. "username" => "root",
  5. "password" => "secret"
  6. ));

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

[php] view plain copy

  1. <?php
  2. $request = $di->get("request");

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

[php] view plain copy

  1. <?php
  2. $request = $di->getRequest();

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

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

[php] view plain copy

  1. <?php
  2. $request = $di->getShared("request");

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

[php] view plain copy

    1. <?php
    2. $component = $di->get("MyComponent", array("some-parameter", "other"))
时间: 2024-11-06 07:19:10

php 依赖注入容器的相关文章

YII框架的依赖注入容器

依赖注入(Dependency Injection,DI)容器就是一个对象,它知道怎样初始化并配置对象及其依赖的所有对象. 所谓的依赖就是,一个对象,要使用另外一个对象才能完成某些功能.那么这个对象就依赖于被使用的对象. 例如: /** * 厨师 */ class cook { /** * 制作食物 */ public function cooking() { $food = new food(); echo $food->get(),"汤<br/>"; } } /*

Unity轻量级依赖注入容器

一.前言 Unity是一个轻量级的可扩展的依赖注入容器,支持构造函数,属性和方法调用注入.在Nuget里安装unity 二.Unity的API方法 UnityContainer.RegisterType<ITFrom,TTO>();  //注册映射 UnityContainer.RegisterType< ITFrom, TTO >("keyName");//注册映射指定key值 IEnumerable<T> databases = UnityCon

依赖注入容器Autofac的详解[转]

依赖注入容器Autofac的详解 发表于 2011 年 09 月 22 日 由 renfengbin 分享到:GMAIL邮箱         Hotmail邮箱 delicious digg Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成,并且开源,Autofac的主要特性如下: 1,灵活的组件实例化:Autofac支持自动装配,给定的组件类型Autofac自动选择使用构造函数注入或者属性注入,Autofac还可以

WPF PRISM开发入门二(Unity依赖注入容器使用)

这篇博客将通过一个控制台程序简单了解下PRISM下Unity依赖注入容器的使用.我已经创建了一个例子,通过一个控制台程序进行加减乘除运算,项目当中将输入输出等都用接口封装后,结构如下: 当前代码可以点击这里下载. 运行效果如下: 下面将引入Unity类库,使用Unity来生成需要的对象实例. 先查看一下CalculateRelpLoop类, public class CalculateRelpLoop : ICalculateRelpLoop { ICalculateService _calcu

依赖注入容器Autofac的详解

Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成,并且开源,Autofac的主要特性如下: 1,灵活的组件实例化:Autofac支持自动装配,给定的组件类型Autofac自动选择使用构造函数注入或者属性注入,Autofac还可以基于lambda表达式创建实例,这使得容器非常灵活,很容易和其他的组件集成.2,资源管理的可视性:基于依赖注入容器构建的应用程序的动态性,意味着什么时候应该处理那些资源有点困难.Autofac

依赖注入容器-Autofac

介绍一款依赖注入的容器AutoFac,一直非常迷惑依赖注入到底有独特的优势或者好处,感觉如果用策略模式和反射等也是可以实现这个解耦的,不管怎么样还是先来大概了解依赖注入到底是怎么一回事.              首先来看个例子,如果你想要一把锤子你会怎么做?(这个例子是我百度上看到的,觉得挺形象的) 1.自己造,打铁,锻造等. 2.或者你找制造锤子的工厂订购 3.打开淘宝,下单,支付 上面的例子在程序开发中分别有什么不同:第一种方式显而易见非常麻烦,从开发角度看就是高度耦合,导致使用和制造混在

php的依赖注入容器

这里接着上一篇 php依赖注入,直接贴出完整代码如下: <?php class C { public function doSomething() { echo __METHOD__, '我是C类|'; } } class B { private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo

IoC 依赖注入容器 Unity

原文:IoC 依赖注入容器 Unity IoC 是什么? 在软件工程领域,“控制反转(Inversion of Control,缩写为IoC)”是一种编程技术,表述在面向对象编程中,可描述为在编译时静态分析器并不知道具体被耦合的对象,而该对象是在运行时被对象装配器绑定的. 在传统编程中,决定业务流程的对象是被静态分配的.而在 IoC 中,业务流程取决于对象装配器实例化提供的对象,这使利用抽象来定义对象间的交互成为可能.对象装配器为了能绑定一个对象,要求该对象必须具备兼容的抽象定义.例如类 Cla

如何编写一个简单的依赖注入容器

随着大规模的项目越来越多,许多项目都引入了依赖注入框架,其中最流行的有Castle Windsor, Autofac和Unity Container. 微软在最新版的Asp.Net Core中自带了依赖注入的功能,有兴趣可以查看这里. 关于什么是依赖注入容器网上已经有很多的文章介绍,这里我将重点讲述如何实现一个自己的容器,可以帮助你理解依赖注入的原理. 容器的构想 在编写容器之前,应该先想好这个容器如何使用. 容器允许注册服务和实现类型,允许从服务类型得出服务的实例,它的使用代码应该像 var

依赖注入和依赖注入容器

http://www.digpage.com/di.html#di 为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Service Locator)两种模式. 关于依赖注入与服务定位器, Inversion of Control Containers and the Dependency Injection pattern 给出了很详细的讲解,这里结合Web应用和Yii具体实现