Laravel框架 - IOC容器详解

news2024/11/28 20:53:35

 IOC 容器代码

好了,说了这么多,下面要上一段容器的代码了. 下面这段代码不是laravel 的源码, 而是来自一本书《laravel 框架关键技术解析》. 这段代码很好的还原了laravel 的服务容器的核心思想. 代码有点长, 小伙伴们要耐心看. 当然小伙伴完全可以试着运行一下这段代码,然后调试一下,这样会更有助于理解.

<?php 
 
//容器类装实例或提供实例的回调函数
class Container {
 
    //用于装提供实例的回调函数,真正的容器还会装实例等其他内容
    //从而实现单例等高级功能
    protected $bindings = [];
 
    //绑定接口和生成相应实例的回调函数
    public function bind($abstract, $concrete=null, $shared=false) {
        
        //如果提供的参数不是回调函数,则产生默认的回调函数
        if(!$concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }
        
        $this->bindings[$abstract] = compact('concrete', 'shared');
    }
 
    //默认生成实例的回调函数
    protected 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;
    }
 
    protected function isBuildable($concrete, $abstract) {
        return $concrete === $abstract || $concrete instanceof Closure;
    }
 
    //获取绑定的回调函数
    protected function getConcrete($abstract) {
        if(!isset($this->bindings[$abstract])) {
            return $abstract;
        }
 
        return $this->bindings[$abstract]['concrete'];
    }
 
    //实例化对象
    public function build($concrete) {
 
        if($concrete instanceof Closure) {
            return $concrete($this);
        }
 
        $reflector = new ReflectionClass($concrete);
        if(!$reflector->isInstantiable()) {
            echo $message = "Target [$concrete] is not instantiable";
        }
 
        $constructor = $reflector->getConstructor();
        if(is_null($constructor)) {
            return new $concrete;
        }
 
        $dependencies = $constructor->getParameters();
        $instances = $this->getDependencies($dependencies);
 
        return $reflector->newInstanceArgs($instances);
    }
 
    //解决通过反射机制实例化对象时的依赖
    protected function getDependencies($parameters) {
        $dependencies = [];
        foreach($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if(is_null($dependency)) {
                $dependencies[] = NULL;
            } else {
                $dependencies[] = $this->resolveClass($parameter);
            }
        }
 
        return (array)$dependencies;
    }
 
    protected function resolveClass(ReflectionParameter $parameter) {
        return $this->make($parameter->getClass()->name);
    }
 
}
 

依赖注入

创建接口 + 实现接口:

1,定义一个名为"Pay"的接口,接口定义了实现类必须提供的方法pay()
2,创建一个实现类Alipay,在这个例子中,Alipay类实现了Pay接口,意味着它必须实现接口中定义的所有方法。
 
//支付类接口
interface Pay
{
    public function pay();
}
 
 
//支付宝支付
class Alipay implements Pay {
      public function __construct(){}
 
      public function pay()
      {
          echo 'pay bill by alipay';
      }
}
//微信支付
class Wechatpay implements Pay  {
      public function __construct(){}
 
      public function pay()
      {
          echo 'pay bill by wechatpay';
      }
}
//银联支付
class Unionpay implements Pay  {
      public function __construct(){}
 
      public function pay()
      {
          echo 'pay bill by unionpay';
      }
}
 
//付款
class PayBill {
 
      private $payMethod;
 
      public function __construct( Pay $payMethod)
      {
          $this->payMethod= $payMethod;
      }
 
      public function  payMyBill()
      {
           $this->payMethod->pay();
      }
}

上面的代码就生成了一个容器,下面是如何使用容器

$app = new Container();
$app->bind("Pay", "Alipay");//Pay 为接口, Alipay 是 class Alipay 支付宝支付
$app->bind("tryToPayMyBill", "PayBill"); //tryToPayMyBill可以当做是Class PayBill 的服务别名
 
//通过字符解析,或得到了Class PayBill 的实例
$paybill = $app->make("tryToPayMyBill"); 
 
//因为之前已经把Pay 接口绑定为了 Alipay,所以调用pay 方法的话会显示 'pay bill by alipay '
$paybill->payMyBill(); 

好了,当我们把容器的概念理解了之后,我们就可以理解下为什么要用接口这个问题了. 如果说我不想用支付宝支付,我要用微信支付怎么办,too easy.

$app->bind("Pay", "Wechatpay");
$app->bind("tryToPayMyBill", "PayBill");
$paybill = $app->make("tryToPayMyBill"); 
$paybill->payMyBill();

是不是很简单呢, 只要把绑定从’Alipay’ 改成 ‘Wechatpay’ 就行了,其他的都不用改. 这就是为什么我们要用接口. 只要你的支付方式继承了pay 这个接口,并且实现pay 这个方法,我们就能够通过绑定正常的使用. 这样我们的程序就非常容易被拓展,因为以后可能会出现成百上千种的支付方式.

逻辑描述

当我们实例化一个Container得到 $app 后, 我们就可以向其中填充东西了

$app->bind("Pay", "Alipay");
$app->bind("tryToPayMyBill", "PayBill"); 

当执行完这两行绑定码后, $app 里面的属性 $bindings 就已经有了array 值,是啥样的呢,我们来看下

array:2 [
 "App\Http\Controllers\Pay" => array:2 [
     "concrete" => Closure {#355 
       class: "App\Http\Controllers\Container" 
       this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …} 
       parameters: array:1 [
         "$c" => []
       ] 
       use: array:2 [
         "$abstract" => "App\Http\Controllers\Pay"
        "$concrete" => "App\Http\Controllers\Alipay"
       ] 
       file: "C:\project\test\app\Http\Controllers\IOCController.php" line:       "119 to 122"
   } 
   "shared" => false 
 ]
 
"tryToPayMyBill" => array:2 [
     "concrete" => Closure {#359 
         class: "App\Http\Controllers\Container" 
         this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …} 
         parameters: array:1 [
               "$c" => []
         ] 
         use: array:2 [
               "$abstract" => "tryToPayMyBill" 
               "$concrete" => "\App\Http\Controllers\PayBill"
         ] 
         file: "C:\project\test\app\Http\Controllers\IOCController.php" line: "119 to 122"
   } 
     "shared" => false 
 ]
]

当执行 $paybill = $app->make(“tryToPayMyBill”); 的时候, 程序就会用make方法通过闭包函数的回调开始解析了.

解析’tryToPayBill’ 这个字符串, 程序通过闭包函数 和build方法会得到 ‘PayBill’ 这个字符串,该字符串保存在$concrete 上. 这个是第一步. 然后程序还会以类似于递归方式 将$concrete 传入 build() 方法. 这个时候build里面就获取了$concrete = ‘PayBill’. 这个时候反射就派上了用场, 大家有没有发现,PayBill 不就是 class PayBill 吗? 然后在通过反射的方法ReflectionClass(‘PayBill’) 获取PayBill 的实例. 之后通过getConstructor(),和getParameters() 等方法知道了 Class PayBill 和 接口Pay 存在依赖

//$constructor = $reflector->getConstructor();
ReflectionMethod {#374 
    +name: "__construct" 
    +class: "App\Http\Controllers\PayBill" 
    parameters: array:1 [
          "$payMethod" => ReflectionParameter {#371 
              +name: "payMethod" 
              position: 0 typeHint: "App\Http\Controllers\Pay"
          }
    ]
     extra: array:3 [
          "file" => "C:\project\test\app\Http\Controllers\IOCController.php"
          "line" => "83 to 86" 
          "isUserDefined" => true 
      ] 
    modifiers: "public"
}
 
 
//$dependencies = $constructor->getParameters();
array:1 [
    0 => ReflectionParameter {#370 
        +name: "payMethod" 
        position: 0 
        typeHint: "App\Http\Controllers\Pay"
        }
]

接着,我们知道了有’Pay’这个依赖之后呢,我们要做的就是解决这个依赖,通过 getDependencies($parameters), 和 resolveClass(ReflectionParameter $parameter) ,还有之前的绑定$app->bind(“Pay”, “Alipay”); 在build 一次的时候,通过 return new $concrete;到这里我们得到了这个Alipay 的实例

if(is_null($constructor)) {
    return new $concrete;
}

到这里我们总算结局了这个依赖, 这个依赖的结果就是实例化了一个 Alipay. 到这里还没结束

$instances = $this->getDependencies($dependencies);

 上面的$instances 数组只有一个element 那就是 Alipay 实例

  array:1 [0 =>Alipay
      {#380}
 ]

最终通过 newInstanceArgs() 方法, 我们获取到了 PayBill 的实例。

 return $reflector->newInstanceArgs($instances);

到这里整个流程就结束了, 我们通过 bind 方式绑定了一些依赖关系, 然后通过make 方法 获取到到我们想要的实例. 在make中有牵扯到了闭包函数,反射等概念.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1028342.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分享VR眼镜加密播放器OEM方案

随着科技的发展&#xff0c;电子成品更新换代很快&#xff0c;用户的娱乐工具也更加多样化&#xff0c;从电脑、手机、平板在到现在使用越来越多的VR眼镜&#xff0c;给用户的体验也越来越多样。而对于内容提供商来说&#xff0c;内容是其竞争优势所在。那么如何做好视频在VR眼…

【Unity】【C#】【VS】如何将VS写的通用C#窗体程序修改为Unity程序

【背景】 需要将一个Visual2019写的C#用Unity改写。 Unity写窗体程序的一大优点就是美观了,看看这默认界面。 但是缺点就是启动时有个Unity过场动画。 【问题】 Unity虽然用的也是C#,但是和Visual2019的通用窗体C#采用的界面显示的库,图像处理,组成一个脚本的基本函数等…

Linux 作业

一. 题目 二.作业内容 第一题&#xff1a; 因老师要求上传安装后远程连接XShell截图&#xff0c;如下&#xff1a; 制作yum缓存&#xff1a;[rootRHEL8 ~]# yum makecache 安装gcc&#xff1a;[rootRHEL8 ~]# yum install gcc -y 制作快照&#xff1a;快照&#xff0c;初始 s…

《rust学习一》 fleet 配置rust环境

最近被网上的rust教徒洗脑了&#xff0c;尝试一下学习rust语言&#xff0c;首先搭建开发环境 准备工作&#xff1a; 安装rust&#xff0c;fleet 第一步&#xff1a;在fleet内打开空文件夹 第二步&#xff1a;创建项目文件 cargo new HelloWorld第三步&#xff1a;Rust&#x…

RFID技术在仓储物流供应链管理中的应用

仓储物流供应链管理的透明度和库存周转率成为管控的重点&#xff0c;为了提高仓储物流的效率和减少库存损失&#xff0c;RFID技术被广泛应用于仓储、分发、零售管理等各个环节&#xff0c;为供应链管理带来了巨大的改变和提升。 首先&#xff0c;采用RFID技术进行仓库物流智能化…

基于SpringBoot+Vue实现的党校培训管理系统源代码+数据库

一、简介 项目简介&#xff1a; 基于微服务架构的党校培训管理系统&#xff0c; 完整代码下载地址&#xff1a;党校培训管理系统 大体总结&#xff1a; 前端使用Vue.js框架&#xff0c;UI组件库使用Element UI与Ant Design Vue&#xff0c;后端基于Spring Boot&#xff0c;使…

verilog学习笔记(1)module实例化2

移位寄存器多路选择器 我的代码&#xff1a; module top_module ( input clk, input [7:0] d, input [1:0] sel, output [7:0] q );wire [7:0] w1;wire [7:0] w2;wire [7:0] w3;my_dff8 my_dff8_1(.clk(clk),.d(d),.q(w1));my_dff8 my_dff8_2(.clk(clk),.d(w1),.q(w2));my_d…

Learn Prompt-提供示例

目前我们与 ChatGPT 交流的主要形式是文字。提示除了指令问题的形式外&#xff0c;还可以包含例子。特别是当我们需要具体的输出时&#xff0c;提供例子可以省去我们对具体任务的解释&#xff0c;帮助ChatGPT更好地理解我们的确切需求&#xff0c;从而提供更准确&#xff0c;更…

如何使用高压放大器驱动高容性负载

使用高压放大器驱动高容性负载是一个具有挑战性的任务&#xff0c;需要仔细考虑电路设计和操作技巧。下面西安安泰Aigtek将为您介绍一些关于如何使用高压放大器驱动高容性负载的方法和注意事项。 首先&#xff0c;让我们了解一下高容性负载。高容性负载通常指电容值较大的负载元…

邮件数据安全案例 | 有一种遇见,叫相见恨晚

Mr.赵回忆和联通相遇的时刻&#xff0c;他说&#xff0c;用一句诗来形容恰如其分&#xff0c;“众里寻他千百度&#xff0c;蓦然回首&#xff0c;那人却在&#xff0c;灯火阑珊处” 。 中国联合网络通信集团有限公司在国内31个省&#xff08;自治区、直辖市&#xff09;和境外…

前后端分离毕设项目之产业园区智慧公寓管理系统设计与实现(内含源码+文档+教程)

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ &#x1f345;由于篇幅限制&#xff0c;想要获取完整文章或者源码&#xff0c;或者代做&am…

Codeforces Round 896 (Div. 1) C. Travel Plan(树形dp+组合数学)

题目 有一棵n(1<n<1e18)个点的树&#xff0c; 点i连着2*i和2*i1两个点&#xff0c;构成一棵完全二叉树 对于每个点i&#xff0c;记其值为a[i]&#xff0c;a[i]可以取[1,m](1<m<1e5)的整数 记i到j的简单路径上的最大值为s[i][j]&#xff0c; 则一棵权值确定的树…

mysql中update更新时加条件和不加条件速度对比

测试时有时需要执行更新操作&#xff0c;想知道大量数据update时加where条件和不加where条件速度差异如何&#xff0c;正好有条件测试&#xff0c;记录一下。 数据&#xff1a;9张表&#xff0c;每张表300w条数据 一、对9张表进行单字段更新时不加条件(如&#xff1a;update …

支付功能、支付平台、支持渠道如何测试?

有学员提问&#xff1a;作为一个支付平台&#xff0c;接入了快钱、易宝或直连银行等多家的渠道&#xff0c;内在的产品流程是自己的。业内有什么比较好的测试办法&#xff0c;来测试各渠道及其支持的银行通道呢&#xff1f; 作为产品&#xff0c;我自己办了十几张银行卡方便测…

web大作业 比赛报名页面+ 团队介绍页面 制作

web大作业 比赛报名页面 团队介绍页面 制作【附源代码】 文章目录 web大作业 比赛报名页面 团队介绍页面 制作【附源代码】前言报名界面效果图如下&#xff1a;代码实现计时器效果实现&#xff08;jqueryboostrap&#xff09; 团队介绍页面模拟框代码&#xff1a;CSS代码 前言 …

android studio 找不到设备

问题描述&#xff1a; 当android studio 没有打开&#xff0c; 执行adb devices 可以查看到设备&#xff0c; 当android studio 打开&#xff0c; 执行adb devices 可以查看不到设备&#xff0c; android studio 设备管理器中也没有设备 解决方法&#xff1a; 关闭android s…

uniapp 开发 之 如何给边框添加阴影效果

uniapp 开发 之 如何给边框添加阴影效果 image.png <view style"width: 100px; height: 100px; margin: 50px; -moz-box-shadow:2px 2px 10px #06c; -webkit-box-shadow:2px 2px 10px #06c; box-shadow:2px 2px 10px #06c; ">测试边框阴影</view>css的bo…

【算法与数据结构】450、LeetCode删除二叉搜索树中的节点

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题首先要分析删除节点的五种情况&#xff1a; 1、没有找到节点2、找到节点 左右子树为空左子树为空…

什么是线上商城?线上商城小程序怎么搭建?

线上商城是一种基于互联网的销售模式。它通过建立线上商店&#xff0c;展示、销售各类商品或服务&#xff0c;方便消费者在线上完成购买和支付。线上商城的出现在很大程度上改变了人们的购物习惯&#xff0c;为商家和消费者提供了更加便捷、高效的交易方式。 线上商城可以是网站…

SAP ALV 报表增删改查 及 下载模板导入文件

选择屏幕设置&#xff1a; 选择屏幕********************************************************************** * SELECTION-SCREEN ********************************************************************** SELECTION-SCREEN BEGIN OF BLOCK BLK1 WITH FRAME TITLE TEXT-001…