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),仅供参考

相关新闻

周报-2026-9-23

周报-2026-9-23

本周主要围绕网上图书商店系统开发项目开展工作,核心任务为完成项目需求分析与需求规格说明书的撰写,全面完成项目前期筹备工作,为后续系统开发、编码测试提供标准化依据。 在本周工作中,我首先对网上图书商店系统的整体业务场景进…

2026/9/24 17:06:14 阅读更多 →
PaddleSpeech 中文多音字注音 G2PW ONNX 推理 API 完全指南

PaddleSpeech 中文多音字注音 G2PW ONNX 推理 API 完全指南

人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation …

2026/9/24 17:06:14 阅读更多 →
绵阳地区装修需要注意的气候与湿度因素

绵阳地区装修需要注意的气候与湿度因素

作为土生土长的绵阳人,身边朋友装修踩的最多的坑,居然都和咱们本地的气候有关——绵阳属亚热带湿润季风气候,年均相对湿度常年维持在76%左右,2-3月回南天、6-7月梅雨季的相对湿度更是能突破92%,去年绵阳家居行业协会的…

2026/9/24 17:06:14 阅读更多 →

最新新闻

MySQL高负载I/O故障全链路排查与优化实战

MySQL高负载I/O故障全链路排查与优化实战

凌晨两点十六分,监控大屏上的MySQL IOPS曲线突然拉成一条垂直的直线,告警声把值班室的安静撕得粉碎。那条从10点开始缓慢抬升的紫色线条,在那一刻直接冲上了磁盘性能的上限刻度,数据库的活跃会话数同步飙到400,大量业务…

2026/9/24 19:49:18 阅读更多 →
企业级数据库工具怎么选?Navicat与NineData对比分析

企业级数据库工具怎么选?Navicat与NineData对比分析

做过十来年数据库运维和研发,Navicat 基本是很多人电脑里的“标配”,从连 MySQL、Oracle 到日常看数据、导数据,确实顺手。但这两年我越来越多地被企业客户问到同一个问题:团队规模上来之后,Navicat 还够用吗&#xff…

2026/9/24 19:49:18 阅读更多 →
MySQL高负载I/O故障根因分析:从系统层到InnoDB的排查与优化

MySQL高负载I/O故障根因分析:从系统层到InnoDB的排查与优化

这事发生在上个月,客户的线上MySQL实例连续两天在业务高峰时段崩溃报警,从应用侧看就是大量请求超时,接口P99延迟从原本的80ms直接飙到3s以上。我看了一眼监控面板,CPU 80%以上,磁盘I/O util触顶100%,iowai…

2026/9/24 19:49:18 阅读更多 →
C++与Python混合编程:pybind11、ctypes、C API选型指南

C++与Python混合编程:pybind11、ctypes、C API选型指南

1. 混合编程的选型困局:为什么三种方案总让人纠结做C和Python混合开发的人,几乎都绕不开一个灵魂拷问:到底用pybind11、ctypes还是Python C API?我最早接触这个领域是做量化回测系统,核心撮合引擎用C写,策略…

2026/9/24 19:49:18 阅读更多 →
MySQL主从架构下MaxScale读写分离与高可用实战指南

MySQL主从架构下MaxScale读写分离与高可用实战指南

搞数据库的兄弟应该都有过这种体验:主从架构搭好了,读写分离却迟迟没落到位。业务代码里手动判断哪个库写、哪个库读,刚开始还行,等Server挂了一台、主从切换过几轮之后,各种连接串了、事务跑飞、延迟把从库拖垮的问题…

2026/9/24 19:49:18 阅读更多 →
MySQL 1251 报错根源与修复:认证插件兼容性实战指南

MySQL 1251 报错根源与修复:认证插件兼容性实战指南

上周五有个朋友发来一张 MySQL 报错截图: ERROR 1251 (08004): Client does not support authentication protocol requested by server; consider upgrading MySQL client 。他说密码确认了好几遍没问题,3306 端口也是通的,但不管是 Navic…

2026/9/24 19:48:17 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →