精华内容
下载资源
问答
  • 理解服务容器的概念,对于我们使用laravel太重要了,应该说是否理解服务容器的概念是区分是否入门laravel的重要条件。因为整个框架正是在服务容器这一基础上构建起来的。 laravel服务容器就像一个高度自动化的...

    如果说laravel框架的核心是什么,那么无疑是服务容器。理解服务容器的概念,对于我们使用laravel太重要了,应该说是否理解服务容器的概念是区分是否入门laravel的重要条件。因为整个框架正是在服务容器这一基础上构建起来的。

     

    laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造。

    因为使用了服务容器,laravel中大部分对象实例化的方式是这样的:

    $obj1 = $container->make('class1', 'class2');
    
    $obj2 = $container->make('class3', 'class4');

    但是在没有使用服务容器的情况下,以下这种方式同样可以做到:

    $obj1 = new class1(new class2());
    
    $obj2 = new class3(new class4());

    那么使用服务容器的优势到底是什么呢?下面我们通过一些具体例子来分析下它的优势:

    例一、发送邮件

    我们把发送邮件的功能封装成一个类,需要使用的时候,实例化并调用发送方法。

    以下是不使用laravel服务容器常见的方式:

    /**
    
     *发送邮件服务类
    
     */
    
    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();

    这使得我们的代码更加简洁了,并且由于有了中间层,灵活性提高了(解耦),那么无论是测试(在测试时我们可以伪造类替换EmailService类)还是优化EmailService类,都变得更加方便。

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

    其他调用的部分我们完全不用动,如果我们没有这个绑定操作,那么不得不在每个使用邮件服务的地方做更改。

    //使用到EamilSerice类的每个地方都要更改
    
    $emailService = new SupperEmailService();
    
    $emailService->send();
    

    例二、实现单例模式

    还是上面的例子,出于性能的考虑,你需要SupperEamilService类实现单例模式,于是在不使用laravel服务容器的情况下,你将SupperEmailService类更改如下:

    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(){
    
             
    
         }
    
     }

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

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

    laravel服务容器天生支持单例,下面是laravel的实现方式:

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

    要实现单例甚至只需要改一行代码,把原来的bind方法改成singleton ,通过容器取出来的便是单例,真是太方便了。

    例三、旅行者去旅行

    这个例子假设一个旅行者去西藏旅行,可以做火车(train)或者走路(leg)去。

    不使用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来实例化对象的时候还是会产生依赖.比如上面trafficTool),这说明我们要创建一个Traveller之前必须得有一个$trafficTool,即Traveller依赖于trafficTool.当使用new来实例化Traveller的时候,Traveller和trafficTool之间就产生了耦合.这样,这两个组件就没办法分开了。

    现在我们来看看使用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服务容器,实际开发中它会给你提供更多的便利。当然它也不是完美无缺的,总之实际使用中扬长避短才是关键。

     

    更多内容请访问

    八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(持续更新)50W年薪挑战!​zhuanlan.zhihu.com图标

    以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处

    展开全文
  • 背景 最近在看项目中前辈的代码,发现使用队列发送消息的时候,从一开始整合发送消息需要数据的时候,就...所以laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造就可以了;而

    背景

    最近在看项目中前辈的代码,发现使用队列发送消息的时候,从一开始整合发送消息需要数据的时候,就一直携带者实例化好的sendMessage类,项目中又将推送资料和推送会议分成了两个队列,所以在好些个方法中都有这个sendMessage对象;而且sendMessage实例化时,又依赖其他的类; 所以这个就让我想起来了laravel的核心 Provider

    了解laravel的工作原理

    详细请看文档

    所以laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造就可以了;而且它可以在实例化的时候解决你需要的一些依赖的类的问题;

    原发送消息执行流程

    • 实际发送逻辑汇总
    SendMessage.php
    
    class SendMessage{
       protected $mailService;  // 发送邮件的服务
       public function __construct(SendMailService $mailService)
        {
            $this->mailService = $mailService;
        }
    
       public function send(array $data, $sender = null, $receiver = null, $module = null, $from = null, $push_url = null, $is_type = false)
        {
        
        }
    }
     
    
    • 队列执行,发送功能
    class RecommendJob implements ShouldQueue
    {
        private $sendMessage;
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
        
        public function __construct($sendMessage, $recommend, $message, $channel, $from= null)
        {
            //
            $this->sendMessage = $sendMessage;
        }
         public function handle()
        {
                $this->sendMessage->send($data,$sender,$receiver)
        }
    }
    

    这里你可以看出,队列在执行发送消息时,是从上一级直接获取的sendMessage实例,也就是说在放入消息进队列的执行了实例化,并携带该对象层层传递,且哪里有消息需要放入队列,哪里就要实例化一次sendMessage;当然这里实例化并不是使用new 的形式来操作的,是使用依赖注入的方式,这也是一种好的方法,

    现发送消息执行流程

    • 实际发送逻辑汇总
      这个我就不用调整了

    • 队列执行,发送功能

    class SendEmail implements ShouldQueue
    {
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    
        public  $user;
        /**
         * Create a new job instance.
         *
         * @return void
         */
        public function __construct($recommend, $message, $channel, $from= null)
        {
            //
            $this->user = $message;
        }
    
        /**
         * Execute the job.
         *
         * @return void
         */
        public function handle()
        {
            //调用发送消息
            app('sendMessage')->send($data, $this->user ,$receiver);
        }
    }
    

    这样在消息入队列的时候,就只需要传递需要的信息,而且在调用队列的时候也不用提前实例。

    主要是当项目执行的时候,会在index.php加载app.php,在这里面检索laravel应用程序的实例。(laravel本身采取的第一个动作就是创建一个应用程序/服务容器)

    所以你需要写一个服务提供者,将事物绑定到服务容器上,然后app才能直接实例;

    服务提供器是所有laravel应用程序的引导中心。引导可以理解为注册,比如注册服务容器绑定、时间监听器、中间件甚至路由等。所以自我新增的服务容器除了要绑定事物外,还要注册,就是放在config/app.php配置文件中去;这时第一步:所有的服务提供者的register方法会被调用,然后等到所有的均注册后,boot方法会被调用;这样就将自己创建的服务容器者放在了laravel中的IOC容器中去了,这样项目执行时,就可以使用app来直接使用新建的服务容器

    新增一个服务提供者

    php artisan make:provider EmailServiceProvide

    class EmailServiceProvider extends ServiceProvider
    {
        /**
         * Register services.
         *
         * @return void
         */
        public function register()
        {
            $this->registerEmailService();
        }
    
        protected function registerEmailService(){
    
    
            $this->app->bind('sendMessage',function () {//单例模式,直接使用singleton方法绑定关系
                return new SendMessage(new MailService());
            });
        }
    
        /**
         * Bootstrap services.
         *
         * @return void
         */
        public function boot()
        {
            //
        }
    }
    

    其实这里bind方法的第一个参数,你可以当作是个key,第二个方法中返回的就是类;这样当调用类的时候,就直接使用app(key)可以了,简单易懂

    在此需要说明的有:
    1.在new SendMessage时,假如这里不仅支持邮件发送,还有短信发送,只需要直接调整为:

    $this->app->bind(‘sendMessage’,function () {//单例模式,直接使用singleton方法绑定关系
    return new SendMessage(new MailService(), new SmsService());
    });

    2.服务提供者是支持单例模式的,所以想调整为单例时,只需要直接将bind调整为singleton方式即可,

    写在最后

    这只是我个人的浅浅的了解,越学习越感觉无知

    展开全文
  • laravel 服务容器容器概念

    千次阅读 2019-11-06 11:38:11
    Laravel 服务容器 发现一篇讲服务容器的文章,讲的很好,转载过来laravel 服务容器 实质是工厂模式的升级,类的传递动态加载 ####以下内容转载 容器,字面上理解就是装东西的东西。常见的变量、对象属性等都...

    Laravel 服务容器

    发现一篇讲服务容器的文章,讲的很好,转载过来laravel 服务容器

    实质是工厂模式的升级,类的传递动态加载


    ####以下内容转载


    容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。本文就从这里开始。

    IoC 容器, laravel 的核心

    Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。

    本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转)DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。

    IoC 容器诞生的故事

    讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。

    超人和超能力,依赖的产生!

    面向对象编程,有以下几样东西无时不刻的接触:接口还有对象。这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例。当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码辅助学习。

    怪物横行的世界,总归需要点超级人物来摆平。

    我们把一个“超人”作为一个类,

    class Superman {}
    

    我们可以想象,一个超人诞生的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个对象,为这个对象定义一个描述他的类吧。一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们以后再丰富:

    class Power {
        /**
         * 能力值
         */
        protected $ability;
    
        /**
         * 能力范围或距离
         */
        protected $range;
    
        public function __construct($ability, $range)
        {
            $this->ability = $ability;
            $this->range = $range;
        }
    }
    

    这时候我们回过头,修改一下之前的“超人”类,让一个“超人”创建的时候被赋予一个超能力:

    class Superman
    {
        protected $power;
    
        public function __construct()
        {
            $this->power = new Power(999, 100);
        }
    }
    

    这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。

    所谓“依赖”,就是 “我若依赖你,我就不能离开你”。

    在一个贯彻面向对象编程的项目中,这样的依赖随处可见。少量的依赖并不会有太过直观的影响,我们随着这个例子逐渐铺开,让大家慢慢意识到,当依赖达到一个量级时,是怎样一番噩梦般的体验。当然,我也会自然而然的讲述如何解决问题。

    一堆乱麻 —— 可怕的依赖

    之前的例子中,超能力类实例化后是一个具体的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完全。我们现在进行修改,我们假设超人可以有以下多种超能力:

    • 飞行,属性有:飞行速度、持续飞行时间
    • 蛮力,属性有:力量值
    • 能量弹,属性有:伤害值、射击距离、同时射击个数

    我们创建了如下类:

    class Flight
    {
        protected $speed;
        protected $holdtime;
        public function __construct($speed, $holdtime) {}
    }
    
    class Force
    {
        protected $force;
        public function __construct($force) {}
    }
    
    class Shot
    {
        protected $atk;
        protected $range;
        protected $limit;
        public function __construct($atk, $range, $limit) {}
    }
    

    *为了省事儿我没有详细写出 __construct() 这个构造函数的全部,只写了需要传递的参数。

    好了,这下我们的超人有点“忙”了。在超人初始化的时候,我们会根据需要来实例化其拥有的超能力吗,大致如下:

    class Superman
    {
        protected $power;
    
        public function __construct()
        {
            $this->power = new Fight(9, 100);
            // $this->power = new Force(45);
            // $this->power = new Shot(99, 50, 2);
            /*
            $this->power = array(
                new Force(45),
                new Shot(99, 50, 2)
            );
            */
        }
    }
    

    我们需要自己手动的在构造函数内(或者其他方法里)实例化一系列需要的类,这样并不好。可以想象,假如需求变更(不同的怪物横行地球),需要更多的有针对性的 新的 超能力,或者需要 变更 超能力的方法,我们必须 重新改造 超人。换句话说就是,改变超能力的同时,我还得重新制造个超人。效率太低了!新超人还没创造完成世界早已被毁灭。

    这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时更换,只需要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。这样的话就不要整个重新来过了。

    对,就是这样的。

    我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。

    工厂模式,依赖转移!

    当然,实现控制反转的方法有几种。在这之前,不如我们先了解一些好玩的东西。

    我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式: 工厂模式

    工厂模式,顾名思义,就是一个类所依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。

    我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅需要通过一个方法:

    class SuperModuleFactory
    {
        public function makeModule($moduleName, $options)
        {
            switch ($moduleName) {
                case 'Fight':   return new Fight($options[0], $options[1]);
                case 'Force':   return new Force($options[0]);
                case 'Shot':    return new Shot($options[0], $options[1], $options[2]);
            }
        }
    }
    

    这时候,超人 创建之初就可以使用这个工厂!

    class Superman
    {
        protected $power;
    
        public function __construct()
        {
            // 初始化工厂
            $factory = new SuperModuleFactory;
    
            // 通过工厂提供的方法制造需要的模块
            $this->power = $factory->makeModule('Fight', [9, 100]);
            // $this->power = $factory->makeModule('Force', [45]);
            // $this->power = $factory->makeModule('Shot', [99, 50, 2]);
            /*
            $this->power = array(
                $factory->makeModule('Force', [45]),
                $factory->makeModule('Shot', [99, 50, 2])
            );
            */
        }
    }
    

    可以看得出,我们不再需要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可满足需求。但这样似乎和以前区别不大,只是没有那么多 new 关键字。其实我们稍微改造一下这个类,你就明白,工厂类的真正意义和价值了。

    class Superman
    {
        protected $power;
    
        public function __construct(array $modules)
        {
            // 初始化工厂
            $factory = new SuperModuleFactory;
    
            // 通过工厂提供的方法制造需要的模块
            foreach ($modules as $moduleName => $moduleOptions) {
                $this->power[] = $factory->makeModule($moduleName, $moduleOptions);
            }
        }
    }
    
    // 创建超人
    $superman = new Superman([
        'Fight' => [9, 100], 
        'Shot' => [99, 50, 2]
        ]);
    

    现在修改的结果令人满意。现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修改了或者增加了新的超能力,只需要针对修改 SuperModuleFactory 即可。扩充超能力的同时不再需要重新编辑超人的类文件,使得我们变得很轻松。但是,这才刚刚开始。

    再进一步!IoC 容器的重要组成 —— 依赖注入!

    由 “超人” 对 “超能力” 的依赖变成 “超人” 对 “超能力模组工厂” 的依赖后,对付小怪兽们变得更加得心应手。但这也正如你所看到的,依赖并未解除,只是由原来对多个外部的依赖变成了对一个 “工厂” 的依赖。假如工厂出了点麻烦,问题变得就很棘手。

    其实大多数情况下,工厂模式已经足够了。工厂模式的缺点就是:接口未知(即没有一个很好的契约模型,关于这个我马上会有解释)、产生对象类型单一。总之就是,还是不够灵活。虽然如此,工厂模式依旧十分优秀,并且适用于绝大多数情况。不过我们为了讲解后面的 依赖注入 ,这里就先夸大一下工厂模式的缺陷咯。

    我们知道,超人依赖的模组,我们要求有统一的接口,这样才能和超人身上的注入接口对接,最终起到提升超能力的效果。

    事实上,我之前说谎了,不仅仅只有一堆小怪兽,还有更多的大怪兽。嘿嘿。额,这时候似乎工厂的生产能力显得有些不足 —— 由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高级的模组加入,我们必须修改工厂类(好比增加新的生产线):

    class SuperModuleFactory
    {
        public function makeModule($moduleName, $options)
        {
            switch ($moduleName) {
                case 'Fight':   return new Fight($options[0], $options[1]);
                case 'Force':   return new Force($options[0]);
                case 'Shot':    return new Shot($options[0], $options[1], $options[2]);
                // case 'more': .......
                // case 'and more': .......
                // case 'and more': .......
                // case 'oh no! its too many!': .......
            }
        }
    }
    

    看到没。。。噩梦般的感受!

    其实灵感就差一步!你可能会想到更为灵活的办法!对,下一步就是我们今天的主要配角 —— DI (依赖注入)

    由于对超能力模组的需求不断增大,我们需要集合整个世界的高智商人才,一起解决问题,不应该仅仅只有几个工厂垄断负责。不过高智商人才们都非常自负,认为自己的想法是对的,创造出的超能力模组没有统一的接口,自然而然无法被正常使用。这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口,自然就可被正常使用。

    interface SuperModuleInterface
    {
        /**
         * 超能力激活方法
         *
         * 任何一个超能力都得有该方法,并拥有一个参数
         *@param array $target 针对目标,可以是一个或多个,自己或他人
         */
        public function activate(array $target);
    }
    

    上文中,我们定下了一个接口 (超能力模组的规范、契约),所有被创造的模组必须遵守该规范,才能被生产。

    其实,这就是 php 中 接口( interface ) 的用处和意义!很多人觉得,为什么 php 需要接口这种东西?难道不是 java 、 C# 之类的语言才有的吗?这么说,只要是一个正常的面向对象编程语言(虽然 php 可以面向过程),都应该具备这一特性。因为一个 对象(object) 本身是由他的模板或者原型 —— 类 (class) ,经过实例化后产生的一个具体事物,而有时候,实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,让大家编写出可以被随时替换却不会产生影响的接口。这种由编程语言本身提出的硬性规范,会增加更多优秀的特性。

    虽然有些绕,但通过我们接下来的实例,大家会慢慢领会接口带来的好处。

    这时候,那些提出更好的超能力模组的高智商人才,遵循这个接口,创建了下述(模组)类:

    /**
     * X-超能量
     */
    class XPower implements SuperModuleInterface
    {
        public function activate(array $target)
        {
            // 这只是个例子。。具体自行脑补
        }
    }
    
    /**
     * 终极炸弹 (就这么俗)
     */
    class UltraBomb implements SuperModuleInterface
    {
        public function activate(array $target)
        {
            // 这只是个例子。。具体自行脑补
        }
    }
    

    同时,为了防止有些 “砖家” 自作聪明,或者一些叛徒恶意捣蛋,不遵守契约胡乱制造模组,影响超人,我们对超人初始化的方法进行改造:

    class Superman
    {
        protected $module;
    
        public function __construct(SuperModuleInterface $module)
        {
            $this->module = $module
        }
    }
    

    改造完毕!现在,当我们初始化 “超人” 类的时候,提供的模组实例必须是一个 SuperModuleInterface 接口的实现。否则就会提示错误。

    正是由于超人的创造变得容易,一个超人也就不需要太多的超能力,我们可以创造多个超人,并分别注入需要的超能力模组即可。这样的话,虽然一个超人只有一个超能力,但超人更容易变多,我们也不怕怪兽啦!

    现在有人疑惑了,你要讲的 依赖注入 呢?

    其实,上面讲的内容,正是依赖注入。

    什么叫做 依赖注入

    本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 。是不是豁然开朗?事实上,就是这么简单。下面就是一个典型的依赖注入:

    // 超能力模组
    $superModule = new XPower;
    
    // 初始化一个超人,并注入一个超能力模组依赖
    $superMan = new Superman($superModule);
    

    关于依赖注入这个本文的主要配角,也就这么多需要讲的。理解了依赖注入,我们就可以继续深入问题。慢慢走近今天的主角……

    更为先进的工厂 —— IoC 容器!

    刚刚列了一段代码:

    $superModule = new XPower;
    
    $superMan = new Superman($superModule);
    

    读者应该看出来了,手动的创建了一个超能力模组、手动的创建超人并注入了刚刚创建超能力模组。呵呵,手动。

    现代社会,应该是高效率的生产,干净的车间,完美的自动化装配。

    一群怪兽来了,如此低效率产出超人是不现实,我们需要自动化 —— 最多一条指令,千军万马来相见。我们需要一种高级的生产车间,我们只需要向生产车间提交一个脚本,工厂便能够通过指令自动化生产。这种更为高级的工厂,就是工厂模式的升华 —— IoC 容器

    class Container
    {
        protected $binds;
    
        protected $instances;
    
        public function bind($abstract, $concrete)
        {
            if ($concrete instanceof Closure) {
                $this->binds[$abstract] = $concrete;
            } else {
                $this->instances[$abstract] = $concrete;
            }
        }
    
        public function make($abstract, $parameters = [])
        {
            if (isset($this->instances[$abstract])) {
                return $this->instances[$abstract];
            }
    
            array_unshift($parameters, $this);
    
            return call_user_func_array($this->binds[$abstract], $parameters);
        }
    }
    

    这时候,一个十分粗糙的容器就诞生了。现在的确很简陋,但不妨碍我们进一步提升他。先着眼现在,看看这个容器如何使用吧!

    // 创建一个容器(后面称作超级工厂)
    $container = new Container;
    
    // 向该 超级工厂 添加 超人 的生产脚本
    $container->bind('superman', function($container, $moduleName) {
        return new Superman($container->make($moduleName));
    });
    
    // 向该 超级工厂 添加 超能力模组 的生产脚本
    $container->bind('xpower', function($container) {
        return new XPower;
    });
    
    // 同上
    $container->bind('ultrabomb', function($container) {
        return new UltraBomb;
    });
    
    // ******************  华丽丽的分割线  **********************
    // 开始启动生产
    $superman_1 = $container->make('superman', ['xpower']);
    $superman_2 = $container->make('superman', ['ultrabomb']);
    $superman_3 = $container->make('superman', ['xpower']);
    // ...随意添加
    

    看到没?通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。发现没有?我们彻底的解除了 超人 与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖!我们通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发。

    这样一种方式,使得我们更容易在创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。

    实际上,真正的 IoC 容器更为高级。我们现在的例子中,还是需要手动提供超人所需要的模组参数,但真正的 IoC 容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。Laravel 框架的服务容器正是这么做的。实现这种功能其实理论上并不麻烦,但我并不会在本文中写出,因为……我懒得写。

    不过我告诉大家,这种自动搜寻依赖需求的功能,是通过 反射(Reflection) 实现的,恰好的,php 完美的支持反射机制!关于反射,php 官方文档有详细的资料,并且中文翻译基本覆盖,足够学习和研究!

    http://php.net/manual/zh/book.reflection.php

    现在,到目前为止,我们已经不再惧怕怪兽们了。高智商人才集思广益,井井有条,根据接口契约创造规范的超能力模组。超人开始批量产出。最终,人人都是超人,你也可以是哦 stuck_out_tongue_closed_eyes

    回归正常世界。我们开始重新审视 laravel 的核心。

    现在,我们开始慢慢解读 laravel 的核心。其实,laravel 的核心就是一个 IoC 容器,也恰好是我之前所说的高级的 IoC 容器。

    可以说,laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如 Route(路由)Eloquent ORM(数据库 ORM 组件)Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 laravel 的服务容器负责的。

    我们以大家最常见的 Route 类作为例子。大家可能经常见到路由定义是这样的:

    Route::get('/', function() {
        // bla bla bla...
    });
    

    实际上, Route 类被定义在这个命名空间:Illuminate\Routing\Router,文件 vendor/laravel/framework/src/Illuminate/Routing/Router.php

    我们通过打开发现,这个类的这一系列方法,如 getpostany 等都不是静态(static)方法,这是怎么一回事儿?不要急,我们继续。

    服务提供者

    我们在前文介绍 IoC 容器的部分中,提到了,一个类需要绑定、注册至容器中,才能被“制造”。

    对,一个类要被容器所能够提取,必须要先注册至这个容器。既然 laravel 称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是 服务提供者(ServiceProvider)

    虽然,绑定一个类到容器不一定非要通过 服务提供者(ServiceProvider)

    但是,我们知道,有时候我们的类、模块会有需要其他类和组件的情况,为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,laravel 将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的 服务提供者

    服务提供者主要分为两个部分,register(注册)boot(引导、初始化),具体参考文档。register 负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分。

    Facade

    我们现在解答之前关于 Route 的方法为何能以静态方法访问的问题。实际上这个问题文档上有写,简单说来就是模拟一个类,提供一个静态魔术方法__callStatic,并将该静态方法映射到真正的方法上。

    我们使用的 Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的 别名 而已,这个类被定义在文件 vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php

    我们打开文件一看……诶?怎么只有这么简单的一段代码呢?

    <?php namespace Illuminate\Support\Facades;
    
    /**
     * @see \Illuminate\Routing\Router
     */
    class Route extends Facade {
    
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'router';
        }
    
    }
    

    其实仔细看,会发现这个类继承了一个叫做 Facade 的类,到这里谜底差不多要解开了。

    上述简单的定义中,我们看到了 getFacadeAccessor 方法返回了一个 route,这是什么意思呢?事实上,这个值被一个 ServiceProvider 注册过,大家应该知道注册了个什么,当然是那个真正的路由类!

    有人会问,Facade 是怎么实现的。我并不想说得太细,一个是我懒,另一个原因就是,自己发现一些东西更容易理解,并不容易忘记。很多细节我已经说了,建议大家自行去研究。

    至此,我们已经讲的差不多了。

    和平!我们该总结总结了!

    无论如何,世界和平了。

    这里要总结的内容就是,其实很多事情并不复杂,怕的是复杂的理论内容。我觉得很多东西一旦想通也就那么回事儿。很多人觉得 laravel 这不好那不好、这里难哪里难,我只能说,laravel 的确不是一流和优秀的框架,说 laravel 是一流、优秀的框架的人,不是 laravel 的粉丝那么就是跟风炒作。Laravel 最大的特点和优秀之处就是使用了很多 php 比较新(实际上并不新)的概念和技术(也就一堆语法糖)而已。因此 laravel 的确符合一个适宜学习的框架。Laravel 的构思的确和其他框架有很大不同,这也要求学习他的人必须熟练 php,并 基础扎实!如果你觉得学 laravel 框架十分困难,那么原因只有一个:你 php 基础不好。

    另外,善于利用命名空间和面向对象的诸多特性,去追寻一些东西,你会发现,原来这一切这么容易。

    个人博客

    展开全文
  • 主要给大家介绍了关于Laravel服务容器绑定的几种方式,文中通过示例代码介绍的非常详细,对大家学习或者使用Laravel具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • 本篇文章给大家带来的内容是关于Laravel服务容器的绑定与解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助
  • 主要介绍了详解如何实现Laravel服务容器的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • laravel 服务容器实现原理

    千次阅读 2019-05-08 18:29:22
    Laravel框架中就是使用服务容器来实现 ** 控制反转 ** 和 ** 依赖注入 **。 什么是控制反转(IoC)和依赖注入(DI) 控制反转(IoC) 就是说把创建对象的** 控制权 进行转移,以前创建对象的主动权和创建时机是由...

    前言

    通过实现laravel 框架功能,以便深入理解laravel框架的先进思想。

    什么是服务容器

    服务容器是用来管理类依赖与运行依赖注入的工具。Laravel框架中就是使用服务容器来实现 ** 控制反转 ** 和 ** 依赖注入 **。

    什么是控制反转(IoC)和依赖注入(DI)

    控制反转(IoC) 就是说把创建对象的** 控制权 进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,也就是 Laravel ** 中的容器。

    依赖注入(DI)则是帮助容器实现在运行中动态的为对象提供提依赖的资源。

    概念容易不太容易让人理解,举个栗子:

    //我们构建一个人的类和一个狗的类
     class People
    {
        public $dog = null;
    
        public function __construct()
        {
            $this->dog = new Dog();
        }
    
        public function putDog(){
            return $this->dog->dogCall();
        }
    
    }
    
    class Dog{
        public function dogCall(){
            return '汪汪汪';
        }
    }

    这个人在遛狗,突然遇到了死对头,他于是放狗咬人

    $people = new People();
    $people->putDog();

    在这个操作中,people类要执行putDog()这个方法,需要依赖Dog类,一般我们像上面一样,在people中利用构造函数来添加这个Dog依赖。如果使用控制反转 依赖注入则是这个样子

    class People
    {
        public $dog = null;
    
        public function __construct(Dog $Dog)
        {
            $this->dog = $dog;
        }
    
        public function putDog(){
            return $this->dog->dogCall();
        }
    
    }

    People类通过构造参数声明自己需要的 依赖类,由容器自动注入。这样就实现了程序的有效解耦,好处在这就不多说了。

    Laravel容器依赖注入的实现

    实现原理需要了解的知识点:

    闭包(匿名函数):
    匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数

    反射:PHP 5 以上版本具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释

    理解了闭包和反射的基本用法我们来看Laravel中是怎么实现容器的,下面代码是我对laravel框架容器部分代码的简化核心版:

    lass Container
    {
        /**
         *  容器绑定,用来装提供的实例或者 提供实例的回调函数
         * @var array
         */
        public $building = [];
    
        /**
         * 注册一个绑定到容器
         */
        public function bind($abstract, $concrete = null, $shared = false)
        {
            if(is_null($concrete)){
                $concrete = $abstract;
            }
    
            if(!$concrete instanceOf Closure){
                $concrete = $this->getClosure($abstract, $concrete);
            }
    
            $this->building[$abstract] =  compact("concrete", "shared");
        }
    
        //注册一个共享的绑定 单例
        public function singleton($abstract, $concrete, $shared = true){
            $this->bind($abstract, $concrete, $shared);
        }
    
        /**
         * 默认生成实例的回调闭包
         *
         * @param $abstract
         * @param $concrete
         * @return Closure
         */
        public function getClosure($abstract, $concrete)
        {
            return function($c) use($abstract, $concrete){
                $method = ($abstract == $concrete)? 'build' : 'make';
    
                return $c->$method($concrete);
            };
        }
    
        /**
         * 生成实例 
         */
        public function make($abstract)
        {
            $concrete = $this->getConcrete($abstract);
    
            if($this->isBuildable($concrete, $abstract)){
                $object = $this->build($concrete);
            }else{
                $object = $this->make($concrete);
            }
    
            return $object;
        }
    
        /**
         * 获取绑定的回调函数
         */
        public function getConcrete($abstract)
        {
            if(! isset($this->building[$abstract])){
                return $abstract;
            }
    
            return $this->building[$abstract]['concrete'];
        }
    
        /**
         * 判断 是否 可以创建服务实体
         */
        public function isBuildable($concrete, $abstract)
        {
            return $concrete === $abstract || $concrete instanceof Closure;
        }
    
        /**
         * 根据实例具体名称实例具体对象
         */
        public function build($concrete)
        {
            if($concrete instanceof Closure){
                return $concrete($this);
            }
    
            //创建反射对象
            $reflector = new ReflectionClass($concrete);
    
            if( ! $reflector->isInstantiable()){
                //抛出异常
                throw new \Exception('无法实例化');
            }
    
            $constructor = $reflector->getConstructor();
            if(is_null($constructor)){
                return new $concrete;
            }
    
            $dependencies = $constructor->getParameters();
            $instance = $this->getDependencies($dependencies);
    
            return $reflector->newInstanceArgs($instance);
    
        }
    
        //通过反射解决参数依赖
        public function getDependencies(array $dependencies)
        {
            $results = [];
            foreach( $dependencies as $dependency ){
                $results[] = is_null($dependency->getClass())
                    ?$this->resolvedNonClass($dependency)
                    :$this->resolvedClass($dependency);
            }
    
            return $results;
        }
    
        //解决一个没有类型提示依赖
        public function resolvedNonClass(ReflectionParameter $parameter)
        {
            if($parameter->isDefaultValueAvailable()){
                return $parameter->getDefaultValue();
            }
            throw new \Exception('出错');
    
        }
    
        //通过容器解决依赖
        public function resolvedClass(ReflectionParameter $parameter)
        {
            return $this->make($parameter->getClass()->name);
    
        }
    
    }

    容器的工作流程

    接着上面遛狗的例子:

    //实例化容器类
    $app =  new Container();
    //向容器中填充Dog
    $app->bind('Dog','App\Dog');
    //填充People
    $app->bind('People', 'App\People');
    //通过容器实现依赖注入,完成类的实例化;
    $people = $app->make('People');
    //调用方法
    echo $people->putDog();

    上面示例中我们先实例化容器类,然后使用bind()方法 绑定接口和 生成相应的实例的闭包函数。然后使用make() 函数生成实例对象,在make()中会调用 isBuildable($concrete, $abstract) 来判断 给定的服务实体($concrete参数)是否可以创建,可以创建 就会调用 build($concrete) 函数 ,build($concrete) 函数会判断传的参数是 是** 闭包 还是 具体类名 **,如果是闭包则直接运行,如果是具体类名的话,则通过反射获取该类的构造函数所需的依赖,完成实例化。

    ** 重点理解 下面这几个函数中 反射的用法,应该就很好理解了 **
    build($concrete) getDependencies(array $dependencies) resolvedNonClass(ReflectionParameter $parameter) resolvedClass(ReflectionParameter $parameter)

    最后

    IoC 理解起来是有点难度,可能文中描述让你感觉不是很清楚,可以将文中代码 在php中用debug观察 运行状态。
    理解了容器的具体实现原理,再去看Laravel中的相关实现,就会感觉豁然开朗。

    原文地址:https://www.cnblogs.com/lilili/p/6953749.html 

    展开全文
  • Laravel:服务容器

    千次阅读 2019-05-29 17:28:08
    我们已经了解了依赖注入及其使用,接下来...这个 IoC 容器Laravel 中被称作服务容器,是整个 Laravel 框架最核心的部分,在它的调度下,框架各个组件可以很好的组合在一起工作。实际上,Laravel 的Application...
  • 文章目录前言容器的定义Laravel容器的解释服务容器几种绑定的方式容器注册容器注入后记 前言 容器的定义 容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于...
  • 我对Laravel服务容器的一些理解 服务容器,又叫IOC容器,其中的核心思想就是控制反转(Inversion Of Control,简称IOC),实现控制反转主要有依赖注入(Dependency Injection,简称DI)和依赖寻找(Dependency Lookup...
  • laravel服务容器

    2018-04-18 15:21:07
    Config下的配置使用可以利用laravel框架中自带的config方法Config下的app.php注册服务-&gt;服务对应在Providers的服务提供者中-&gt;所有的服务提供者都继承自Illuminate\Support\ServiceProvider类。大部分...
  • 刚刚接触laravel,对于laravel服务容器不是很理解。看了《Laravel框架关键技术解析》和网上的一些资料后对于服务容器有了一些自己的理解,在这里分享给大家 1、依赖 IoC模式主要是用来解决系统组件之间相互依赖...
  • laravel 服务容器

    2020-07-21 16:36:32
    因为使用了服务容器laravel中大部分对象实例化的方式是这样的: $obj1 = $container->make('class1', 'class2'); $obj2 = $container->make('class3', 'class4');...那么使用服务容器...
  • http://it.51xw.net/language/1000bo.html
  • 关于laravel服务(容器)和服务提供者及绑定 Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。 参考:https://laravelacademy.org/post/9534.html <?php namespace App\Http\Controllers\UcServer...
  • I found another topic as same as I'm asking here (<a href="https://stackoverflow.com/questions/54018582/laravel-5-7-service-container-and-service-provider">Laravel 5.7 (Service Container and Service ...
  • 这段代码来自《laravel 框架关键技术解析》书中的对框架容器框架的提炼,很好的还原了laravel服务容器的核心思想,有兴趣的童鞋可以自己执行并研究下代码的逻辑。 <?php interface Visit { public ...
  • Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。依赖注入听上去很花哨,其实质是通过构造函数或者某些情况下通过「setter」方法将类依赖注入到类中。 简单的说服务容器就是管理类的依赖和执行依赖...
  • laravel IoC容器心得

    千次阅读 2018-01-24 13:33:52
    前言 laravel现在已经是一个非常成熟的PHP框架,在学习laravel的这一段时间内,laravel...在刚刚开始使用laravel的时候,说实话,阅读laravel文档有关服务容器服务提供者的章节的时候是很迷茫的。由于其中涉及了很多
  • Laravel 服务容器是用于管理类的依赖和执行依赖注入的工具。 依赖注入的本质是通过构造函数或者某些情况下通过 setter 方法,将类的依赖注入到类中。 来看一个简单的例子: &lt;?php namespace App\...
  • Laravel 服务容器学习

    2019-08-29 16:02:28
    容器绑定创建位置 一般情况下载 app/Providers 下面 新建 AdminDataProvider.php <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; // 引入 服务提供者 class AdminDataProvider ...
  • 【转载】:原文在这里
  • 对于laravel服务容器不是很理解看了《Laravel框架关键技术解析》和网上的一些资料后对于服务容器有了一些自己的理解,在这里分享给大家,过程比较曲折,看了比较长的时间,包括回调函数,use用法,反射函数等需要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,733
精华内容 2,293
关键字:

laravel服务容器的使用