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 提供组件服务的核心对象它在框架中的实现为yii\di\ServiceLocator。本指南以官方文档《服务定位器》为骨架结合framework/di/ServiceLocator.php、framework/base/Module.php、framework/base/Application.php等源码与tests/framework/di/ServiceLocatorTest.php测试用例系统讲解组件注册的四种方式、懒加载与单例语义、components批量配置、模块树遍历Tree traversal以及如何替换内置应用组件帮助你彻底掌握 Yii 2 中组件装配与取用的底层机制。什么是服务定位器服务定位器是一个了解如何提供各种应用所需的服务或组件的对象。它维护一张组件 ID → 组件定义的映射表每个组件在定位器中都只有一个单独的实例并通过唯一 ID 标识使用者只需通过 ID 即可从定位器中取到组件而无需关心组件是如何创建、如何配置的。在 Yii 2 中服务定位器是[[yii\di\ServiceLocator]]框架实现或其子类的一个实例。从类继承关系看ServiceLocator继承自yii\base\Component因此它本身具备属性、事件、行为等组件能力见 ServiceLocator.phpyii\base\Module继承自ServiceLocator而yii\base\Application又继承自Module因此应用对象和所有模块对象本质上都是服务定位器见 Module.php 相关注释。最常用的服务定位器application 对象Yii 应用中最常用的服务定位器是application应用对象可以通过\Yii::$app访问。它所提供的服务被称为application components应用组件比如request、response、urlManager、db、cache等。通过服务定位器提供的功能你可以非常容易地配置这些组件甚至用你自己的实现替换它们。此外每个模块对象本身也是一个服务定位器yii\base\Module extends ServiceLocator这为下文将要介绍的模块树遍历打下了基础。注册组件set() 的四种定义方式要使用服务定位器第一步是注册相关组件。组件可以通过[[yii\di\ServiceLocator::set()]]方法源码进行注册。set($id, $definition)接受两类参数$id组件 ID如db、cache$definition组件定义可以是类名、配置数组、PHP 回调可调用对象、对象实例四种形式之一。use yii\di\ServiceLocator; use yii\caching\FileCache; $locator new ServiceLocator; // 通过一个可用于创建该组件的类名注册 cache缓存组件。 $locator-set(cache, yii\caching\ApcCache); // 通过一个可用于创建该组件的配置数组注册 db数据库组件。 $locator-set(db, [ class yii\db\Connection, dsn mysql:hostlocalhost;dbnamedemo, username root, password , ]); // 通过一个能返回该组件的匿名函数注册 search 组件。 $locator-set(search, function () { return new app\components\SolrService; }); // 用组件实例注册 pageCache 组件 $locator-set(pageCache, new FileCache);四种定义类型的源码级语义从set()的实现framework/di/ServiceLocator.php可以归纳出每种类型的处理逻辑定义类型判定条件行为类名 / 可调用对象is_object($definition) \|\| is_callable($definition, true)直接存入_definitionsget()时经Yii::createObject()实例化或直接调用回调配置数组is_array($definition)必须包含class键否则抛出InvalidConfigExceptionThe configuration for the ... component must contain a class element对象实例是对象且非Closureget()时原样返回该实例共享同一对象null$definition null等价于注销从_definitions中删除该 ID配置数组还支持 DI 3.0 风格的__class键用于指定最终实例化的子类见 testDi3Compatibilityset()会把__class的值复制到class键后再存储这为声明时用父类、运行时用子类的替换场景提供了便利。若定义类型不在上述范围内例如传了字符串但不可调用同样会抛出InvalidConfigExceptionUnexpected configuration type...。获取组件get() 与属性式访问一旦组件被注册成功你可以任选以下两种方式之一通过它的 ID 访问它$cache $locator-get(cache); // 或者 $cache $locator-cache;如上所示[[yii\di\ServiceLocator]]允许通过组件 ID像访问一个属性值那样访问组件。这一能力来自对__get()魔术方法的覆写framework/di/ServiceLocator.php当访问的属性名是已注册组件 ID 时直接转调get($name)否则回退到父类Component::__get()的常规属性解析。与之配套的__isset()同文件 L87-L94也优先判断组件是否存在。懒加载与单例语义服务定位器的核心特性之一是懒加载当你第一次访问某组件时get()会通过该组件的注册信息创建一个该组件的实例并把它缓存进_components数组之后再次访问则直接返回同一个实例单例语义。对应的源码在 framework/di/ServiceLocator.php若_components[$id]已存在已实例化直接返回若_definitions[$id]存在但未实例化定义为对象且非Closure即传入的对象实例时直接把它写入_components并返回其余定义类名、配置数组、回调统一交给Yii::createObject($definition)创建对象后缓存并返回若 ID 不存在且$throwException为true默认抛出InvalidConfigException: Unknown component ID: $id若为false则返回null。同一个实例这一行为被测试用例 testShared 明确验证连续两次get()返回的对象使用assertSame断言完全相等同一引用。而 testObject 则验证了注册对象实例后取回的正是原对象。has() 与 clear()检查与移除你可以通过[[yii\di\ServiceLocator::has()]]检查某组件 ID 是否被注册源码$locator-has(cache); // 是否存在该 ID 的定义 $locator-has(cache, true); // 是否已经实例化该组件has($id, $checkInstance false)默认检查的是是否有定义_definitions当第二个参数为true时检查的是是否已实例化_components二者结果可能不同。若你用一个无效的 ID 调用get()则会抛出InvalidConfigException异常。此外还有clear($id)方法同文件 L221-L224可同时移除定义与已实例化的对象用于重置某组件。通过 components 属性批量注册因为服务定位器经常会在创建时附带配置信息因此 Yii 提供了一个可写的属性[[yii\di\ServiceLocator::setComponents()|components]]以便配置该属性或一次性注册多个组件。底层对应setComponents($components)方法framework/di/ServiceLocator.php它本质上是set()的批量版本遍历数组逐项调用set($id, $component)。下面的代码展示了如何用一个配置数组配置一个应用并注册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); // ... other initializations ... return $solr; }, ], ];注意getComponents($returnDefinitions true)同文件 L231-L234可以取回当前已注册的定义列表默认或已实例化的组件列表便于调试与检查。用静态构建器封装复杂初始化在上面的代码中还有一个替代方法来配置search组件与其直接写一个 PHP 回调来建立SolrService实例不如使用一个静态类方法来返回这样的回调class SolrServiceBuilder { public static function build($ip) { return function () use ($ip) { $solr new app\components\SolrService($ip); // ... other initializations ... return $solr; }; } } return [ // ... components [ // ... search SolrServiceBuilder::build(127.0.0.1), ], ];当你发布一个封装了非 Yii 第三方库的组件时这种替代方法是最好的使用者无需理解第三方库的复杂构建逻辑只要调用静态方法即可完成组件配置。这与set()中对PHP 可调用的宽泛支持一致——回调可以是匿名函数也可以是[Foo, bar]形式的类方法数组测试用例 testCallable 同时验证了匿名函数与静态方法两种形式。遍历树Tree traversal模块间的组件查找模块允许任意嵌套Yii 应用程序本质上是一棵模块树。由于这些模块中的每一个都是服务定位器所以子模块有权限访问其父模块这允许模块使用$this-get(db)而不是引用根服务定位器Yii::$app-get(db)。额外的好处是开发者可以在子模块中覆盖模块的配置。源码中的查找链从 2.0.13 版本起模块的get()与has()都被重写以支持向父模块逐级回溯framework/base/Module.phppublic 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时最终抛出Unknown component ID异常。在 ServiceLocator.php 的类注释中也有明确说明Modules add tree traversal for service resolution模块为服务解析增加了树遍历能力。配置不会向上合并需要特别注意模块中组件的配置决不会与来自父模块中相同 ID 组件的配置合并。也就是说如果子模块定义了db那么子模块内取到的db完全按子模块的定义创建父模块应用中db的配置不会影响它。Service Locator 模式允许我们定义命名服务但不能假定具有相同名称的服务使用相同的配置参数。这一设计保证了模块的封装性与可覆盖性子模块可以整体替换父模块的某个组件而不会产生隐式的配置污染。应用组件核心组件注册与覆盖作为应用对象yii\web\Application/yii\console\Application它本身就是一个服务定位器。应用在初始化阶段会把核心组件与用户自定义组件合并注册framework/base/Application.php// merge core components with custom components foreach ($this-coreComponents() as $id $component) { if (!isset($config[components][$id])) { $config[components][$id] $component; } elseif (is_array($config[components][$id]) !isset($config[components][$id][class])) { $config[components][$id][class] $component[class]; } }而coreComponents()framework/base/Application.php默认注册的核心组件包括组件 ID默认类logyii\log\Dispatcherviewyii\web\Viewformatteryii\i18n\Formatteri18nyii\i18n\I18NurlManageryii\web\UrlManagerassetManageryii\web\AssetManagersecurityyii\base\Security若检测到yii\swiftmailer\Mailer存在还会追加mailer组件。合并逻辑保证了如果你没有在components中配置某个核心组件框架会替你注册默认实现如果你配置了但未指定class框架会补上默认类名。这也是为什么你可以用如下方式替换内置组件——例如把cache换成文件缓存、把urlManager换成自定义实现return [ components [ cache [ class yii\caching\FileCache, ], urlManager [ class app\components\MyUrlManager, enablePrettyUrl true, ], ], ];通过服务定位器应用组件可以被非常容易地配置或替换文档原文可以通过服务定位器所提供的功能非常容易地配置这些组件或甚至是用你自己的实现替换掉他们。常见问题与最佳实践1. 使用无效 ID 调用get()会发生什么抛出InvalidConfigException: Unknown component ID: xxx。若希望返回null而非抛异常可传第二个参数$locator-get(xxx, false)。2. 属性式访问$locator-cache与$locator-get(cache)有什么区别功能上等价。前者依赖__get()魔术方法先走has()判定后者直接走get()逻辑。当组件 ID 与某个真实属性同名时组件访问优先。3. 为什么多次get()返回同一个对象因为get()首次创建实例后写入_components缓存后续直接命中缓存返回参见 get() 源码 与测试 testShared。这意味着组件默认是共享单例适合承载db连接、缓存等有状态服务。4. 在模块中如何获取父模块应用的组件直接用$this-get(db)即可模块的get()会自动向父模块回溯Module.php。若要在子模块中替换组件只需在子模块的components中定义同名 ID。5. 服务定位器与依赖注入容器yii\di\Container是什么关系二者协同工作ServiceLocator负责按 ID 提供命名服务注册-查找模式而组件的实际创建最终委托给Yii::createObject()后者经由 DI 容器解析类依赖并完成实例化见 Container::get() 与 Container::set()。你可以通过应用的container配置项预注册依赖绑定再由服务定位器按 ID 取用。6. 何时适合用服务定位器框架内部大量场景应用组件、模块组件都依赖它在自己封装扩展时也常用组件 ID 静态构建器的写法见上文 SolrServiceBuilder 示例让使用者通过配置而非代码获得服务。但要避免把全局服务定位器当作依赖注入的替代品滥用否则会掩盖类的真实依赖关系。小结本文围绕yii\di\ServiceLocator及其在 Yii 2 中的实际形态完整覆盖了服务定位器的定义与三种典型存在形式应用对象、模块对象、独立定位器、set()注册的四种定义类型及其校验规则、get()的懒加载与单例语义、has()/clear()/getComponents()等辅助 API、components属性的批量注册、静态构建器封装第三方库的最佳实践以及模块树遍历中子模块向上查找、配置不合并的底层机制。以上内容既有官方文档的完整实操示例也有 ServiceLocator.php、Module.php、Application.php 的源码佐证与 ServiceLocatorTest.php 的测试印证可作为你在 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 服务定位器Service Locator完全指南从 yii\di\ServiceLocator 到应用组件的注册、解析与模块树遍历Yii 2 服务定位器Service Locator完全指南从 yii\di\ServiceLocator 到应用组件的注册、解析与模块树遍历 导读 服务后端Web框架上一篇Mylar3 开源项目安装与使用指南下一篇Hoppscotch本地 API 调试完整指南五分钟上手免费开源创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表