Laravel核心 - 服务容器


Laravel的服务容器是什么

laravel的服务容器是用于管理类的依赖和执行依赖注入的工具。依赖注入实质上是指:类的依赖项通过构造函数或者某些情况下通过setter方法注入到类中

setter就是类似魔术方法__set的方法,也可以自定义

服务容器的核心功能是IoC容器用以解决依赖注入,控制反转 IoC模式又称依赖注入模式

依赖注入是指组件的依赖通过外部以参数或其他形式注入

为什么使用服务容器

使用这种方式,一方面降低了代码之间的耦合,另一方面也提高了代码的可维护性与拓展性

服务容器的应用场景

类似发邮件服务的全局服务

我们把发送邮件的功能封装成一个类,需要使用的时候,实例化并调用发送方法
下面是php代码的简单实现

/**
 *发送邮件服务类
 */
class EmailService{
    public function send(){
        //todo 发送邮件方法
    }
}

//如果任何地方要发邮件我们就复制下面这两行代码
$emailService = new EmailService();
$emailService->send();

但是使用了laravel的服务容器之后

$this->app->bind('emailService', function ($app) {
    return new EmailService();
});

//如果任何地方要发邮件我们就复制下面这两行代码
$emailService = app('emailService');
$emailService->send();

这就使我们的代码更加简洁了,由于有了中间层,灵活性提高了,那么无论测试或者优化我们的服务类都会变得更方便

//只需要改这一个地方
$this->app->bind('emailService', function ($app) {
    return new SupperEmailService();
});

其他调用的地方我们完全不用动,如果我们没有这个绑定操作(中间层),那么我们不得不在每个使用服务的地方进行修改,这样就会很繁琐.

实现单例模式

还是使用上面的邮件服务例子,可能处于性能的考虑,需要这个服务实现单例模式,于是在不适用laravel服务容器的情况下,做出如下更改

class SupperEamilService{
      //创建静态私有的变量保存该类对象
     static private $instance;

      //防止直接创建对象
      private function __construct(){

     }
         //防止克隆对象
     private function __clone(){

     }
     static public function getInstance(){
                 //判断$instance是否是Uni的对象
                 //没有则创建
         if (!self::$instance instanceof self) {
             self::$instance = new self();
         }
         return self::$instance;

     }

     //发送邮件方法
     public function send(){

     }
 }

除此之外,由于服务类的构造函数为私有,无法通过new关键字来实例化对象,所以在每个实例化的地方调用都需要改成这样

$emailService=SupperEmailService::getInstance();
$emailService->send();

但是laravel的服务容器天生支持单例

//只需要把bind改成singleton 
$this->app->singleton('emailService', function ($app) {
    return new SupperEmailService();
});

我们只需要把原来的bind方法改成singleton,通过容器取出来的就是单例了

旅行者去旅行

这个例子就是讲旅行和旅行工具之间的服务解耦.

假设一个旅行者去西藏旅行,可以坐火车或者走路去

不使用laravel服务容器:

<?php

interface TrafficTool
{
  public function go();
}

class Train implements TrafficTool
{

  public function go()
  {
  echo "train....";
  }
}

class Leg implements TrafficTool
{
  public function go()
  {
  echo "leg..";
  }
}

class Traveller
{
  /**
  * @var Leg|null|Train
  * 旅行工具
  */
  protected $_trafficTool;

  public function __construct(TrafficTool $trafficTool)
  {
  $this->_trafficTool = $trafficTool;
  }

  public function visitTibet()
  {
  $this->_trafficTool->go();
  }
}

当旅行者要坐或者去旅行,通常我们这样写

<?php
 $train = new Train();
$tra = new Traveller($train);
$tra->visitTibet();

事实上这种写法非常不错了,因为对于旅行工具的依赖已经通过接口的方式转移到外部了。但是使用new来实例化的时候还是会产生依赖。也就是说Train和Traveller之间产生了耦合

那么laravel服务容器怎么实现的?

<?php
namespace App\Providers;

use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
  public function register()
  {
     //在服务容器中绑定类
     $this->app->bind( 'TrafficTool', 'Train');
     $this->app->bind('Traveller', 'Traveller');
  }
}

实例化对象

<?php
// 实例化对象
$tra = app()->make('Traveller');
$tra->visitTibet(); 

当我们使用服务容器获取旅行类的对象时,容器会自动注入对象所需要的参数,而在此之前我只需要绑定特定的类就可以了,这样才体现了真正的自动化,是的旅行和旅行工具完全解耦了,当我们需要更改旅行方式的时候,只需要更改绑定就好了

参考资料:
使用laravel服务容器的优势

应用场景的总结

1,应用于服务之间的解耦
2,方便的实现单例模式

服务容器原理

反射

laravel服务容器的一个强大的特性就是能通过反射自动解析依赖。反射具有检测类及其方法的能力,比如PHP中的ReflectionClass类允许你检测一些方法在给定的类中是否可用.

通过PHP的这种特性,laravel可以实现一些有趣的功能!

class UserController extends BaseController {

    public function __construct(StripeBiller $biller)
    {
        $this->biller = $biller;
    }

}

如上控制器初始化时需要传入StripeBiller类型的对象,我们可以通过反射进行类型检测。当laravel容器没有绑定相应的解析器,他就会通过反射尝试解析该类.流程大致如下

  1. 服务容器有无StripeBiiler解析器?(就是是否在服务容器绑定过)
  2. 没有解析器?映射类StripeBiller判断其依赖
  3. 递归的解析StripeBiller类的所有依赖
  4. 通过ReflectionClass->newInstanceArgs()实例化一个新的StripeBiller
    可以看到服务容器为我们做了许多繁重的工作,使你能释放更多时间用于编码各种逻辑的代码类。

    掌握容器,先查看下容器的源码IlluminateContainerContainer这个类文件


文章作者: 我若为侠
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 我若为侠 !
 上一篇
Laravel核心 - 门面Facades Laravel核心 - 门面Facades
Laravel的门面Facades是什么门面为应用服务容器的绑定类提供了一个静态接口。Laravel内置了很多门面,你可能在不知道的情况下正在使用它们。laravel的门面作为服务容器中低层类的静态代理,相比于传统静态方法,在维护时能够提供
2020-07-12
下一篇 
Nginx配置反向代理 Nginx配置反向代理
简单的lnamp的nginx反向代理转发访问站点由nginx服务器接受请求并转发给局域网里的某台lamp的服务器。 部署过程1,正常的部署一台nginx服务器以及一台lamp服务器,他们在一个局域网里面,然后再nginx服务器里面的ngin
  目录