在Yii中,组件是非常重要的概念。Yii中每种类型的应用程序都在源码中定义了必不可少的核心组件(core components)。可以说,组件是构成完整Yii应用程序的基石。
Yii2中,组件是通过DI(Dependency Injection)和SL(Service Locator)来实现的。关于DI和SL,可查看Yii2权威指南中的章节:http://www.yiiframework.com/doc-2.0/guide-concept-di-container.html和http://www.yiiframework.com/doc-2.0/guide-concept-service-locator.html。英文不太好的同学,可以查看深入理解Yii2.0这本书中的设计模式一章(想深入理解Yii2.0,推荐收藏和阅读该书的所有章节)。有了组件,在Yii2.0的程序中,我们能非常方便的注册和获取依赖对象,实现程序的解耦。
Yii2.0中定义和使用组件非常的方便:在config文件中,如下形式即定义了组件:
return [ ...., // other configuration 'components' => [ 'xxx' => [ // xxx is the id/alias/name of the component 'class' => 'full qualified class name', // unnecessary for core components, 'prop1' => 'value1', 'prop2' => 'value2', ..., // other public properties values, ], ..., // other components config ], ..., // extra settings ];
由于Application是Service Locator的子类,从而在程序中可以方便的引用组件: Yii::$app->xxx
(Yii2.0中重载了__set魔术方法,如果为了效率,可以使用Yii::$app->getXxx()
来获取组件引用)。
最近在看Spring,想到单例的问题,并与Yii2.0进行了对比。对比发现,Yii2.0中通过配置方式定义组件有如下缺点:
-
组件都是单例。在web开发中,很多组件确实只需单例模式就够了,比如request, response对象。然而也有非单例的需求,例如数据库(非web应用中,多数据库连接/连接池就比较好使)。
-
无法引用已经定义的组件。这个说法不太准确,已经定义的组件可以作为某个属性传入新的组件中;但如果新组件的构造函数依赖于其他组件,在配置中无法做到。
这两个问题促使我又仔细阅读了Yii中的相关概念。通过指南和和API文档,发现Yii确实是实现了IoC和DI。不过由于Yii2.0是全栈式的框架(开箱即用),平常用的又是基于Service Locator的组件,导致对Yii2.0中的DI了解甚少。下面是本次学习和实验的收获:
-
Yii2.0中确实实现了DI,并且有DI容器。默认的DI容器可通过
Yii::$container
引用; -
通过DI容器,可以注册和定义依赖。使用的时候,DI容器会自动解析依赖并返回所需要的实例;
-
通过DI容器(而非Service Locator),可以让依赖每次都返回新的实例,也可以指定只返回单例;
-
Yii2.0的Service Locator返回的对象通过DI容器来解析依赖并生成,组件都是单例。
现在回到博文的主题,也就是文章中提出的两个问题:如何定义每次返回新实例的依赖以及引用DI中的其他依赖?答案在于直接引用DI容器而非通过配置文件定义组件。下面是示例:
// 定义测试类 class Dep {} Class Foo { private $dateTime; private $dep; public function __construct($ts, $dep) { $this->dateTime = date('Y-m-d H:i:s', $ts); $this->dep = $dep; } public function printDate() { echo $this->dateTime, PHP_EOL; } } // 下面是测试 ... // 引入必要的类和配置文件 // 注册依赖 $container = Yii::$container; $container->set('foo', function ($container, $params, $config) { return new \Foo(time(), $container->get('dep')); }); $container->setSingleton('dep', 'Dep'); // 测试示例是否为新的 $container->get('foo')->printDate(); sleep(2); $container->get('foo')->printDate();
以下是程序输出:
2016-04-16 16:51:54 2016-04-16 16:51:56
通过示例输出,可以证实DI容器确实按照预期返回了两个新的实例。同时,使用DI容器可以直接定义依赖于其他对象的组件,并且与注册顺序无关。直接使用DI容器的一个不便之处当然是无法通过Yii::$app->xxx
这种方式引用,只能是Yii::$container->get('xxx')
了。
原文链接:https://www.qiquanji.com/post/4709.html
本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。
微信扫码关注
更新实时通知