深入理解 ThinkPHP:容器和依赖注入的原理与实践
容器和依赖注入
ThinkPHP使用容器来更方便的管理类依赖及运行依赖注入,新版的容器支持PSR-11
规范。
容器类的工作由
think\Container
类完成,但大多数情况我们只需要通过app
助手函数或者think\App
类即可容器操作,如果在服务类中可以直接调用this->app
进行容器操作。
依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成。
<?php namespace app\controller; use think\Request; class Index { protected $request; protected $services; public function __construct(Request $request,TestServices $services) { $this->request = $request; $this->services = $services; } public function hello($name) { return 'Hello,' . $name . '!This is '. $this->request->action(); } }
依赖注入的对象参数支持多个,并且和顺序无关。
支持使用依赖注入的场景包括(但不限于):
- 控制器架构方法;
- 控制器操作方法;
- 路由的闭包定义;
- 事件类的执行方法;
- 中间件的执行方法;
对于自定义的类以及方法,如果需要使用依赖注入,需要使用系统提供的invoke
助手函数调用,例如:
class Foo { public function __construct(Bar $bar) { } }
如果直接new
的话,需要手动传入Bar
对象实例
$bar = new Bar(); $foo = new Foo($bar);
如果使用容器来实例化的话,可以自动进行依赖注入。
$foo = invoke('Foo');
如果要对某个方法支持依赖注入,可以使用
class Foo { public function bar(Bar $bar) { // ... } }
$result = invoke(['Foo', 'bar']);
也支持对某个函数或者闭包使用依赖注入
$result = invoke(function(Bar $bar) { // ... });
绑定
依赖注入的类统一由容器进行管理,大多数情况下是在自动绑定并且实例化的。不过你可以随时进行手动绑定类到容器中(通常是在服务类的register
方法中进行绑定),支持多种绑定方式。
绑定类标识
可以对已有的类库绑定一个标识(唯一),便于快速调用。
// 绑定类库标识 $this->app->bind('think\Cache', 'app\common\Cache');
或者使用助手函数
// 绑定类库标识 bind('cache', 'think\Cache');
绑定的类标识可以自己定义(只要不冲突)。
绑定闭包
可以绑定一个闭包到容器中
bind('sayHello', function ($name) { return 'hello,' . $name; });
绑定实例
也可以直接绑定一个类的实例
$cache = new think\Cache; // 绑定类实例 bind('cache', $cache);
绑定至接口实现
对于依赖注入使用接口类的情况,我们需要告诉系统使用哪个具体的接口实现类来进行注入,这个使用可以把某个类绑定到接口
// 绑定think\LoggerInterface接口实现到think\Log bind('think\LoggerInterface','think\Log');
使用接口作为依赖注入的类型
<?php namespace app\index\controller; use think\LoggerInterface; class Index { public function hello(LoggerInterface $log) { $log->record('hello,world!'); } }
批量绑定
在实际应用开发过程,不需要手动绑定,我们只需要在app
目录下面定义provider.php
文件(只能在全局定义,不支持应用单独定义),系统会自动批量绑定类库到容器中。
return [ 'route' => \think\Route::class, 'session' => \think\Session::class, 'url' => \think\Url::class, ];
绑定标识调用的时候区分大小写,系统已经内置绑定了核心常用类库,无需重复绑定
系统内置绑定到容器中的类库包括
系统类库 | 容器绑定标识 |
---|---|
think\App | app |
think\Cache | cache |
think\Config | config |
think\Cookie | cookie |
think\Console | console |
think\Db | db |
think\Debug | debug |
think\Env | env |
think\Event | event |
think\Http | http |
think\Lang | lang |
think\Log | log |
think\Middleware | middleware |
think\Request | request |
think\Response | response |
think\Filesystem | filesystem |
think\Route | route |
think\Session | session |
think\Validate | validate |
think\View | view |
解析
使用app
助手函数进行容器中的类解析调用,对于已经绑定的类标识,会自动快速实例化
$cache = app('cache');
带参数实例化调用
$cache = app('cache',['file']);
对于没有绑定的类,也可以直接解析
$arrayItem = app('org\utils\ArrayItem');
调用和绑定的标识必须保持一致(包括大小写)
容器中已经调用过的类会自动使用单例,除非你使用下面的方式强制重新实例化。
// 每次调用都会重新实例化 $cache = app('cache', [], true);
对象化调用
使用app
助手函数获取容器中的对象实例(支持依赖注入)。
$app = app(); // 判断对象实例是否存在 isset($app->cache); // 注册容器对象实例 $app->cache = think\Cache::class; // 获取容器中的对象实例 $cache = $app->cache;
也就是说,你可以在任何地方使用app()
方法调用容器中的任何类,但大多数情况下面,我们更建议使用依赖注入。
// 调用配置类 app()->config->get('app_name'); // 调用session类 app()->session->get('user_name');
自动注入
容器主要用于依赖注入,依赖注入会首先检查容器中是否注册过该对象实例,如果没有就会自动实例化,然后自动注入,例如:
我们可以给路由绑定模型对象实例
Route::get('user/:id','index/Index/hello') ->model('\app\index\model\User');
然后在操作方法中自动注入User模型
<?php namespace app\index\controller; use app\index\model\User; class Index { public function hello(User $user) { return 'Hello,'.$user->name; } }
自定义实例化
容器中的对象实例化支持自定义,可以在你需要依赖注入的对象中增加__make
方法定义,例如:
如果你希望User
模型类在依赖注入的时候 使用自定义实例化的方式,可以用下面的方法。
<?php namespace app\index\model; use think\Model; use think\db\Query; class User extends Model { public static function __make(Query $query) { return (new self())->setQuery($query); } }
容器对象回调机制
容器中的对象实例化之后,支持回调机制,利用该机制可以实现诸如注解功能等相关功能。
你可以通过resolving
方法注册一个全局回调
Container::getInstance()->resolving(function($instance,$container) { // ... });
回调方法支持两个参数,第一个参数是容器对象实例,第二个参数是容器实例本身。
或者单独注册一个某个容器对象的回调
Container::getInstance()->resolving(\think\Cache::class,function($instance,$container) { // ... });

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
AI 智能体到底应该如何构建?分享 Github 上收获 4k stars 的 12 条原则
编者按: AI 智能体到底应该如何构建?是追求复杂的端到端解决方案,还是回归软件工程的本质思维? 我们今天为大家带来的文章,作者的观点是:智能体本质上就是软件,应该用严谨的软件工程原则来构建,而非盲目追求"黑箱式"的复杂框架。 文章从智能体的发展历程出发,深入剖析了从有向图到 DAG 编排工具,再到今天 AI 智能体的技术演进脉络。随后,作者系统性地提出了构建可靠 LLM 应用的12个核心原则。 这篇文章为正在构建 AI 应用的开发者提供了一套完整而实用的方法论,特别适合那些希望摆脱框架束缚、追求技术本质的工程师。相信通过这12个原则的指导,我们能够构建出更加可靠、可控、可扩展的智能体系统。 01 AI Agent 是如何走到今天的 1.1 我的观点仅供参考 无论您是智能体领域的新手,还是像我这样固执的老兵,我都将试图说服您摒弃对 AI Agent 的大部分固有认知,退一步,从第一性原理(first principles)出发重新思考它们。(如果你错过了不久前 OpenAI 发布的内容,这里有个剧透预警:把更多智能体逻辑塞进 API 后面并非正解) 02 智能体本质上是软件,让我们简要...
- 下一篇
3 个超火的开源项目「GitHub 热点速览」
说到 MP4,你首先会想到什么?可能不少人首先想到的都是"小电影",但最近横空出世的 Memvid 项目却刷新了大家的认知------它巧妙地将 MP4 文件变成了 AI 记忆库,让视频文件不仅仅是用来观看,更能为 AI 提供持久、高效的记忆能力。Google 不仅在大模型方面持续发力,同时也在端侧小模型领域不断探索。他们最近开源的 gallery 是用 Kotlin 写的手机应用,能让用户在手机端离线体验各种 Edge AI 模型,一经开源便迅速登上 GitHub 热榜。 最后,必须得说一下这个开源项目 ChinaTextbook,它是一个完全免费的国内教材资源集合,涵盖了从小学、中学到大学,各学科的 PDF 教材。这个项目默默维护了整整 5 年,最近才终于被更多人所发现,一个月内增长了近 3 万 Star。作者的初衷是想让更多人能为兴趣而读书,而非仅仅是为了应试。 本文目录 热门开源项目 1.1 让 MP4 成为你的 AI 记忆库:Memvid 1.2 一键生成软件物料清单的工具:syft 1.3 极简的项目管理工具:PLANKA 1.4 手机上离线体验 AI 模型:gallery...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7,8上快速安装Gitea,搭建Git服务器