ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Yii 2 Service Locator 全面解析:应用组件注册、获取与模块树遍历机制

Yii 2 Service Locator 全面解析:应用组件注册、获取与模块树遍历机制 后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载Service Locator服务定位器是 Yii 2 框架依赖管理体系的核心设计模式它通过 ID 向应用提供统一的服务获取入口。本文将基于 docs/guide-pt-BR/concept-service-locator.md 与框架源码系统讲解如何在 Yii 2 中注册、配置与获取组件深入剖析\Yii::$app背后的单例共享机制、components批量配置方式以及模块间的树遍历查找规则帮助读者彻底掌握应用组件与模块组件的底层工作原理。什么是 Service LocatorService Locator 是一个知道如何提供应用可能需要的各类服务或称组件的对象。在 Service Locator 中每个组件只存在一个实例并通过唯一的ID进行标识。你使用这个 ID 从 Service Locator 中取回对应组件。在 Yii 2 中Service Locator 就是yii\di\ServiceLocator类本身或其任意子类的一个实例。其核心职责非常清晰维护一张组件 ID → 组件定义的注册表在首次访问时按定义创建组件实例并在后续访问中复用同一实例对外暴露get()、set()、has()等统一的组件管理接口。Yii 2 中最常用的 Service Locator 是应用对象application可通过\Yii::$app全局访问。应用对象提供的服务被称为应用组件application components例如request、response、urlManager、db、cache等。借助 Service Locator 提供的能力你可以轻松地配置这些组件甚至用自己的实现替换它们。除了应用对象之外每个模块module对象本身也是一个 Service Locator——从源码可以看到framework/base/Module.php中Module类直接继承自ServiceLocatorclass Module extends ServiceLocator这意味着模块可以注册自己的私有组件也可以沿模块树向上查找父级包括应用注册的组件具体机制将在下文树遍历小节展开。注册组件set() 的四种方式使用 Service Locator 的第一步是向它注册组件。注册通过ServiceLocator::set()方法完成。下面的代码展示了注册组件的四种不同方式use yii\di\ServiceLocator; use yii\caching\FileCache; $locator new ServiceLocator; // 方式一使用类名字符串get() 时会自动实例化该类 $locator-set(cache, yii\caching\ApcCache); // 方式二使用配置数组用于创建并配置组件 $locator-set(db, [ class yii\db\Connection, dsn mysql:hostlocalhost;dbnamedemo, username root, password , ]); // 方式三使用匿名函数回调返回一个组件实例 $locator-set(search, function () { return new app\components\SolrService; }); // 方式四直接注册一个对象实例 $locator-set(pageCache, new FileCache);从源码看 set() 的合法定义类型ServiceLocator::set()的实现明确了组件定义definition的四种合法形态并给出了严格的校验逻辑定义类型说明内部处理类名字符串如yii\caching\ApcCache属于可调用callable形态直接存入_definitionsPHP 回调匿名函数或[Foo, bar]形式的类方法数组存入_definitionsget()时调用返回对象配置数组必须包含class元素存入_definitionsget()时交给Yii::createObject()创建对象实例已实例化好的对象存入_definitionsget()时原样返回几个值得注意的实现细节重复注册会覆盖set()首先执行unset($this-_components[$id])清除该 ID 下已缓存的实例再写入新定义若传入null则相当于注销该组件。配置数组必须含class若配置数组既没有class也没有__class元素set()会抛出InvalidConfigExceptionThe configuration for the ... component must contain a class element.。从testDi3Compatibility测试可以看出__class是兼容旧版 DI 写法的别名键最终会被统一改写为class。其他非法类型如整数、字符串但非类名等会抛出InvalidConfigException提示 Unexpected configuration type。ServiceLocator内部使用两个私有数组维护状态_components存放已实例化的组件按 ID 索引_definitions存放组件定义。这两个数组正是理解单例共享机制的关键。获取组件get() 与属性访问组件注册完成后即可用其 ID 通过以下两种等价方式获取$cache $locator-get(cache); // 或者 $cache $locator-cache;第二种写法之所以可行是因为ServiceLocator重写了__get()魔术方法framework/di/ServiceLocator.php当访问的属性名是一个已注册的组件 ID 时直接委托给get($name)返回组件否则回退到父类Component::__get()处理普通属性。同理__isset()也被重写使得isset($app-inputWidget)这类判断对组件同样生效见testModulePropertyIsset测试。共享单例机制get()方法 的核心逻辑如下public function get($id, $throwException true) { if (isset($this-_components[$id])) { // 已实例化过直接返回缓存实例共享单例 return $this-_components[$id]; } if (isset($this-_definitions[$id])) { $definition $this-_definitions[$id]; if (is_object($definition) !$definition instanceof Closure) { // 定义本身就是对象直接缓存并返回 return $this-_components[$id] $definition; } // 其余定义类名/配置数组/回调统一交给 Yii::createObject() return $this-_components[$id] Yii::createObject($definition); } elseif ($throwException) { throw new InvalidConfigException(Unknown component ID: $id); } return null; }可以看到首次访问时创建get()首次被调用时会根据注册信息创建组件新实例并返回此后再次访问时直接从_components返回同一个实例——这就是 Service Locator 的单例语义。测试tests/framework/di/ServiceLocatorTest.php中的testShared用assertSame($object, $object2)验证了多次get()返回的是同一对象。延迟实例化组件只在被真正访问时才创建未被访问的注册组件不会实例化因此不会产生无谓的内存与性能开销。无效 ID 抛出异常若调用get()时传入一个未注册的 ID会抛出InvalidConfigException异常消息为 Unknown component ID: ...。若将第二个参数$throwException设为false则返回null而不是抛异常。检查组件是否已注册has()你可以使用has()检查某个组件 ID 是否已经注册if ($locator-has(cache)) { // 已注册 }has($id, $checkInstance false)的行为取决于第二个参数默认false检查是否有组件定义传true则检查是否已实例化即存在于_components中。通过 components 配置批量注册组件由于 Service Locator 通常是通过配置创建的ServiceLocator专门提供了可写属性components对应setComponents()方法允许一次性配置并注册多个组件。下面的配置数组展示了如何为一个 Service Locator例如一个应用注册db、cache、tz和search组件return [ // ... components [ db [ class yii\db\Connection, dsn mysql:hostlocalhost;dbnamedemo, username root, password , ], cache yii\caching\ApcCache, tz function() { return new \DateTimeZone(Yii::$app-formatter-defaultTimeZone); }, search function () { $solr new app\components\SolrService(127.0.0.1); // ... 其他初始化 ... return $solr; }, ], ];setComponents()的实现非常简单——遍历数组并对每一项调用set()因此数组中每个值的写法规则与set()完全一致类名、配置数组、回调、实例均可混用。与之对应的getComponents($returnDefinitions true)则用于取出当前全部定义或已加载实例列表。用静态工厂方法替代回调封装第三方库的推荐姿势上面示例中配置search组件还有一种替代思路不必直接写一个构建SolrService实例的 PHP 回调而是用一个类的静态方法返回这样的回调如下所示class SolrServiceBuilder { public static function build($ip) { return function () use ($ip) { $solr new app\components\SolrService($ip); // ... 其他初始化 ... return $solr; }; } } return [ // ... components [ // ... search SolrServiceBuilder::build(127.0.0.1), ], ];这种替代方式在你发布一个封装了第三方库的 Yii 组件时最值得采用把构造第三方对象的复杂逻辑收敛到静态方法内部组件使用者只需调用一次静态方法即可完成配置无需了解第三方库的初始化细节。树遍历模块中组件查找的父子传递模块允许任意嵌套——一个 Yii 应用本质上就是一棵模块树。由于每个模块都是 Service Locator让子模块能够访问父模块就变得顺理成章。这样模块内部可以直接使用$this-get(db)而无需绕道引用根定位器Yii::$app-get(db)。额外的好处是开发者可以在模块中覆盖父级同名组件的配置。当某个模块无法满足一个组件的请求时该请求会沿模块树向上传递给父模块继续查找。这一行为的源码位于Module::get()与Module::has()public function get($id, $throwException true) { if (!isset($this-module)) { return parent::get($id, $throwException); } $component parent::get($id, false); if ($component null) { $component $this-module-get($id, $throwException); } return $component; } public function has($id, $checkInstance false) { return parent::has($id, $checkInstance) || (isset($this-module) $this-module-has($id, $checkInstance)); }其查找顺序可以概括为先在当前模块自身注册的组件中查找未命中则递归地向父模块查找父模块可能是上一级模块也可能是应用本身一直向上直到根级若仍找不到且$throwException为true则抛出InvalidConfigException。该树遍历能力自 Yii 2.0.13 版本起提供见Module::get()的文档注释。需要特别注意一点模块中组件的配置永远不会与父模块中同名组件的配置合并。Service Locator 模式允许定义具名服务但不能假设同名服务使用了相同的配置参数——模块可以在components中为db提供一套全新的定义来覆盖父级定义这是有意为之的设计。应用核心组件预注册机制作为最常用的 Service Locator应用对象在初始化时就会预注册一组核心组件。Application::coreComponents()framework/base/Application.php返回的默认核心组件包括组件 ID类logyii\log\Dispatcherviewyii\web\Viewformatteryii\i18n\Formatteri18nyii\i18n\I18NurlManageryii\web\UrlManagerassetManageryii\web\AssetManagersecurityyii\base\Securitymailer当yii\swiftmailer\Mailer类存在时yii\swiftmailer\Mailer在Application::preInit()中framework/base/Application.php框架会把核心组件与用户自定义组件合并如果配置中没有某个核心组件 ID则自动补上默认定义如果用户为某个核心组件 ID 提供了配置数组但没有指定class则沿用核心组件的默认类用户只需覆盖其余属性。这也是为什么你可以在config里只写components [ cache [ class yii\caching\FileCache, ], ],而不必重新定义view、urlManager等组件的原因。此外ServiceLocator本身继承自yii\base\Component因此它还具备事件event与行为behavior能力可与其他 Yii 组件一样被扩展。测试佐证共享、回调与对象注册仓库的单元测试tests/framework/di/ServiceLocatorTest.php从多个角度验证了上述行为testCallable分别用匿名函数和[Creator, create]静态方法数组注册验证get()能正确创建带属性初始化的实例testObject注册已实例化对象后get()返回的assertSame同一对象testShared注册配置数组后两次get()返回同一实例印证共享单例语义testModulePropertyIsset验证isset()与属性访问对组件的可用性testDi3Compatibility验证__class键与class键的兼容处理。这些测试与 framework/di/ServiceLocator.php 的实现相互印证可以作为理解本文所讲机制的第一手参考。小结Yii 2 的 Service Locator 是一个按 ID 提供单例服务的轻量级容器通过set()/setComponents()注册通过get()或属性语法获取通过has()查询配合延迟实例化与共享缓存实现高效的组件生命周期管理。应用对象与模块对象都是它的实例前者预注册了log、view、urlManager等核心组件后者则通过树遍历沿父子链向上解析服务。掌握这些机制你就能在配置文件中灵活地注册、覆盖和替换应用组件也能在模块化开发中正确理解同名组件的覆盖规则从而真正驾驭 Yii 2 的应用架构。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 服务定位器Service Locator深入解析从组件注册到模块树遍历Yii 2 服务定位器Service Locator深入解析从组件注册到模块树遍历 服务定位器Service Locator是 Yii 2 框架依赖管后端Web框架Yii 2 服务定位器Service Locator深度解析组件注册、单例共享与模块树遍历Yii 2 服务定位器Service Locator深度解析组件注册、单例共享与模块树遍历 服务定位器Service Locator是 Yii 2 框后端Web框架Yii 2 应用组件Application Components全面解析服务定位、注册方式与核心组件配置实战Yii 2 应用组件Application Components全面解析服务定位、注册方式与核心组件配置实战 应用组件Application Compo后端Web框架上一篇EmojiOne Color彩色表情字体终极免费跨平台表情解决方案指南下一篇Broadcast Box社区贡献指南如何参与开源项目并提交PR创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表