容器和依赖注入
未知
2021-07-04 10:16:01
0

容器和依赖注入

ThinkPHP使用容器来更方便的管理类依赖及运行依赖注入,新版的容器支持PSR-11规范。

容器类的工作由think\Container类完成,但大多数情况我们只需要通过app助手函数或者think\App类即可容器操作,如果在服务类中可以直接调用this->app进行容器操作。

依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成。

<?php
namespace app\controller;

use think\Request;

class Index
{
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    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) {
    // ...
});
复制

上一篇:URL访问

下一篇:服务

相关内容

巴西音乐人邀请西安外事鼓乐...
巴西音乐家与西安外事鼓乐团合作,共谱文化交响,融合东西方音乐元素,...
2024-11-20 21:23:35
云服务器上Kubernet...
本文介绍了在云服务器上搭建Kubernetes集群的实战过程,包括...
2024-11-20 17:00:41
云服务器上Kubernet...
本指南详述了云服务器上Kubernetes集群的部署与运维步骤,包...
2024-11-20 16:23:44
Kubernetes在云服...
Kubernetes性能优化策略包括:资源合理分配、节点自动扩展、...
2024-11-20 16:00:41
云服务器上Kubernet...
摘要:本文详解了云服务器上Kubernetes集群的部署方案,包括...
2024-11-20 15:00:48
PHP与SQLSRV数据库...
摘要:本文介绍了PHP与SQLSRV数据库迁移的步骤和注意事项,包...
2024-11-20 11:46:42

热门资讯

tp6开发规范 命名规范 请理解并尽量遵循以下命名规范,可以减少在开发过程中出现不必要的错误。 ThinkPHP6....
6高级查询 快捷查询 快捷查询方式是 一种多字段相同查询条件 的简化写法,可以进一步简化查询条件的写法,在多个字...
tp6多应用提示控制器不存在:... 第一个情况是没有使用composer安装扩展。 如果要使用多应用模式, 你需要win+r,cmd指针...
6配置 配置目录 单应用模式 对于单应用模式来说,配置文件和目录很简单,根目录下的 config 目录下面就...
6异常处理 和PHP默认的异常处理不同,ThinkPHP抛出的不是单纯的错误信息,而是一个人性化的错误页面。 异...
6swoole 本篇内容主要讲述了最新的 think-swoole 扩展的使用。目前仅支持Linux环境或者MacO...
6助手函数 助手函数 系统为一些常用的操作方法封装了助手函数,便于使用,包含如下: 助手函数 描述 abort ...
6查询表达式 查询表达式 查询表达式支持大部分的SQL查询语法,也是 ThinkPHP 查询语言的精髓,查询表达式...
6路由参数 路由参数 路由分组及规则定义支持指定路由参数,这些参数主要完成路由匹配检测以及后续行为。 路由参数可...
6查询 模型查询和数据库查询方法的区别主要在于,模型中的查询的数据在获取的时候会经过获取器的处理,以及更加对...