容器

ThinkPHP6框架全新重构,以容器的方式来管理所有需要用到的类库。我理解的容器概念如下:

  • 容器内有两个数组

    • 一个组数是定义容器内所有类的标识和对应的类完整类名的数组,我们暂且定义这个数组是$provider,
    • 另一个数组是依据$provider得到类标识与类单例的数组,我们暂且定义为$container
    • 容器的$provider数组的key和value默认是相同的
    • 容器的$container数组的key和$provider的key相同
    • 容器的$container数组的value就是经过单例模式得到的$provider的value的类的实例化
    • 容器使用$provider得到$container是框架最底层的操作,它最先执行

      1. // 用代码简单示意一下
      2. <?php
      3. Class Container
      4. {
      5. public $provider = [
      6. 'think\exception\Handle' => think\exception\Handle::class,
      7. ];
      8. public $container = [];
      9. public function __constract()
      10. {
      11. foreach($provider as $flag=>$prov){
      12. $this->container[$flag] = 得到类单例方法($prov)
      13. }
      14. }
      15. }
  • 容器就是存储具有key-value数据结构的,用于存储需要用到的所有类标识和类实例的一个大集合
  • tp6中,默认在容器中添加了很多的类实例,ThinkPHP6官方手册中的[系统内置绑定到容器中的类库包括]的表格中说明了标识和系统类库对应的关系

依赖注入

  • 在容器中存入了很多key-value形式的类单例,就可以很方便得实现依赖注入。
  • 比如在控制器的某个方法中,使用依赖注入的方式,参数设置一个类名,其实这个类的单例在框架进行初始化时,就已经放在容器中了,且调用方式是通过类的完整类名字符串进行索引就可以得到类的单例

服务和门面

服务和门面要一起来解释

基础概念理解

先理解三个概念名词:

  • 服务具体实现者
    服务具体实现者,往往是一个类。它无需继承任何基类,只需要实现服务的具体方法。比如Email邮件服务,它仅需要实现如下方法
    • 设置基础配置方法
    • 获取基础配置方法
    • 发送邮件方法
  • 服务提供者
    服务提供者,就是ThinkPHP6官方手册上提到的服务概念。他和服务门面一样,是一种把服务具体实现者,注入到容器中的一种方式。
  • 服务门面
    服务门面就是ThinkPHP6官方手册中提到的门面概念。他和服务提供者一样,是一种把服务具体实现者,注入到容器中的一种方式。

服务提供者服务门面的相同点

  • 服务提供者服务门面都是ThinkPHP6框架提供修改容器中两个数组$provider和$container的方式

服务提供者服务门面的区别

  • 服务提供者
    与框架基础类库一起,统一在容器中进行初始化
  • 服务门面
    当你在首次调用服务门面时,才会将服务门面在容器中进行初始化

ThinkPHP6实现服务提供者的两种方式

  • 第一种:修改文件/app/provider.php,直接定义容器中$provider数组需要array_merge的数组内容

在Laytp框架中,默认的/app/provider.php文件内容如下:

  1. <?php
  2. use app\exception\Http;
  3. // 容器Provider定义文件
  4. return [
  5. 'think\exception\Handle' => Http::class,
  6. ];

/app/provider.php为空时,其实在ThinkPHP6的容器类的$provider数组中,已经有值'think\exception\Handle' => think\exception\Handle::class,当框架出现异常时,会使用默认的异常接管类进行异常处理,而当/app/provider.php有值时,容器在得到容器中$container数组之前会先将容器中的$provider数组与/app/provider.php文件中的数组进行合并后,再初始化$container数组的值,这样就达到了外部修改容器中类单例的效果

  • 第二种,编写服务提供者类,并继承框架提供的Service基类,且实现register方法

下面是官方文档中FileSystemService的代码内容

  1. <?php
  2. namespace app\service;
  3. use my\util\FileSystem;
  4. class FileSystemService extends Service
  5. {
  6. public function register()
  7. {
  8. $this->app->bind('file_system', FileSystem::class);
  9. }
  10. }

ThinkPHP6实现服务门面

ThinkPHP6中以服务门面的方式修改容器中的类单例,仅一种方式。就是定义门面类并继承Facade基类,以及在门面类中实现getFacadeClass方法,下面是官方举例的Test门面类代码

  1. <?php
  2. namespace app\facade;
  3. use think\Facade;
  4. class Test extends Facade
  5. {
  6. protected static function getFacadeClass()
  7. {
  8. return 'app\common\Test';
  9. }
  10. }

服务提供者服务门面如何选择

在Laytp中,没有用服务提供者,服务全部使用服务门面的方式将服务具体实现者注入进了容器。
因为,我们现在使用ThinkPHP6,基本还是会使用php-fpm来运行程序。容器的初始化是每次请求都会执行的,使用服务提供者的方式来将服务具体实现者注入进容器,意味着,每次请求都会实例化所有服务类并注册进容器。服务越多,初始化消耗越大

关于文件后缀名

Laytp中,/app/service/目录下存放了所有的服务具体实现者和对应的服务门面

服务具体实现者没有文件后缀名。

服务门面的文件后缀名是ServiceFacade

预留了Service这个后缀给想要使用服务提供者的同学们。

那么什么时候来用服务提供者呢?

第一种

如果你使用think-swoole来运行ThinkPHP6程序,应该使用服务提供者的方式,因为使用swoole来运行PHP程序时,Web服务启动后,容器中的单例会一直存在于内存中,不会每次请求都初始化。

第二种

特别喜欢使用函数参数进行类的依赖注入写法的同学。服务提供者注入服务具体实现者到容器,才能在方法的参数位置直接依赖注入服务提供者来调用服务具体实现者的方法

  • 评论列表0