• Laravel框架 - IOC容器详解


     IOC 容器代码

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

    1. //容器类装实例或提供实例的回调函数
    2. class Container {
    3. //用于装提供实例的回调函数,真正的容器还会装实例等其他内容
    4. //从而实现单例等高级功能
    5. protected $bindings = [];
    6. //绑定接口和生成相应实例的回调函数
    7. public function bind($abstract, $concrete=null, $shared=false) {
    8. //如果提供的参数不是回调函数,则产生默认的回调函数
    9. if(!$concrete instanceof Closure) {
    10. $concrete = $this->getClosure($abstract, $concrete);
    11. }
    12. $this->bindings[$abstract] = compact('concrete', 'shared');
    13. }
    14. //默认生成实例的回调函数
    15. protected function getClosure($abstract, $concrete) {
    16. return function($c) use ($abstract, $concrete) {
    17. $method = ($abstract == $concrete) ? 'build' : 'make';
    18. return $c->$method($concrete);
    19. };
    20. }
    21. public function make($abstract) {
    22. $concrete = $this->getConcrete($abstract);
    23. if($this->isBuildable($concrete, $abstract)) {
    24. $object = $this->build($concrete);
    25. } else {
    26. $object = $this->make($concrete);
    27. }
    28. return $object;
    29. }
    30. protected function isBuildable($concrete, $abstract) {
    31. return $concrete === $abstract || $concrete instanceof Closure;
    32. }
    33. //获取绑定的回调函数
    34. protected function getConcrete($abstract) {
    35. if(!isset($this->bindings[$abstract])) {
    36. return $abstract;
    37. }
    38. return $this->bindings[$abstract]['concrete'];
    39. }
    40. //实例化对象
    41. public function build($concrete) {
    42. if($concrete instanceof Closure) {
    43. return $concrete($this);
    44. }
    45. $reflector = new ReflectionClass($concrete);
    46. if(!$reflector->isInstantiable()) {
    47. echo $message = "Target [$concrete] is not instantiable";
    48. }
    49. $constructor = $reflector->getConstructor();
    50. if(is_null($constructor)) {
    51. return new $concrete;
    52. }
    53. $dependencies = $constructor->getParameters();
    54. $instances = $this->getDependencies($dependencies);
    55. return $reflector->newInstanceArgs($instances);
    56. }
    57. //解决通过反射机制实例化对象时的依赖
    58. protected function getDependencies($parameters) {
    59. $dependencies = [];
    60. foreach($parameters as $parameter) {
    61. $dependency = $parameter->getClass();
    62. if(is_null($dependency)) {
    63. $dependencies[] = NULL;
    64. } else {
    65. $dependencies[] = $this->resolveClass($parameter);
    66. }
    67. }
    68. return (array)$dependencies;
    69. }
    70. protected function resolveClass(ReflectionParameter $parameter) {
    71. return $this->make($parameter->getClass()->name);
    72. }
    73. }

    依赖注入

    创建接口 + 实现接口:
    
    1,定义一个名为"Pay"的接口,接口定义了实现类必须提供的方法pay()
    2,创建一个实现类Alipay,在这个例子中,Alipay类实现了Pay接口,意味着它必须实现接口中定义的所有方法。
    1. //支付类接口
    2. interface Pay
    3. {
    4. public function pay();
    5. }
    6. //支付宝支付
    7. class Alipay implements Pay {
    8. public function __construct(){}
    9. public function pay()
    10. {
    11. echo 'pay bill by alipay';
    12. }
    13. }
    14. //微信支付
    15. class Wechatpay implements Pay {
    16. public function __construct(){}
    17. public function pay()
    18. {
    19. echo 'pay bill by wechatpay';
    20. }
    21. }
    22. //银联支付
    23. class Unionpay implements Pay {
    24. public function __construct(){}
    25. public function pay()
    26. {
    27. echo 'pay bill by unionpay';
    28. }
    29. }
    30. //付款
    31. class PayBill {
    32. private $payMethod;
    33. public function __construct( Pay $payMethod)
    34. {
    35. $this->payMethod= $payMethod;
    36. }
    37. public function payMyBill()
    38. {
    39. $this->payMethod->pay();
    40. }
    41. }

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

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

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

    1. $app->bind("Pay", "Wechatpay");
    2. $app->bind("tryToPayMyBill", "PayBill");
    3. $paybill = $app->make("tryToPayMyBill");
    4. $paybill->payMyBill();

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

    逻辑描述

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

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

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

    1. array:2 [
    2. "App\Http\Controllers\Pay" => array:2 [
    3. "concrete" => Closure {#355
    4. class: "App\Http\Controllers\Container"
    5. this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …}
    6. parameters: array:1 [
    7. "$c" => []
    8. ]
    9. use: array:2 [
    10. "$abstract" => "App\Http\Controllers\Pay"
    11. "$concrete" => "App\Http\Controllers\Alipay"
    12. ]
    13. file: "C:\project\test\app\Http\Controllers\IOCController.php" line: "119 to 122"
    14. }
    15. "shared" => false
    16. ]
    17. "tryToPayMyBill" => array:2 [
    18. "concrete" => Closure {#359
    19. class: "App\Http\Controllers\Container"
    20. this:Container{[#354](http://127.0.0.4/ioc#sf-dump-254248394-ref2354) …}
    21. parameters: array:1 [
    22. "$c" => []
    23. ]
    24. use: array:2 [
    25. "$abstract" => "tryToPayMyBill"
    26. "$concrete" => "\App\Http\Controllers\PayBill"
    27. ]
    28. file: "C:\project\test\app\Http\Controllers\IOCController.php" line: "119 to 122"
    29. }
    30. "shared" => false
    31. ]
    32. ]

    当执行 $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 存在依赖

    1. //$constructor = $reflector->getConstructor();
    2. ReflectionMethod {#374
    3. +name: "__construct"
    4. +class: "App\Http\Controllers\PayBill"
    5. parameters: array:1 [
    6. "$payMethod" => ReflectionParameter {#371
    7. +name: "payMethod"
    8. position: 0 typeHint: "App\Http\Controllers\Pay"
    9. }
    10. ]
    11. extra: array:3 [
    12. "file" => "C:\project\test\app\Http\Controllers\IOCController.php"
    13. "line" => "83 to 86"
    14. "isUserDefined" => true
    15. ]
    16. modifiers: "public"
    17. }
    18. //$dependencies = $constructor->getParameters();
    19. array:1 [
    20. 0 => ReflectionParameter {#370
    21. +name: "payMethod"
    22. position: 0
    23. typeHint: "App\Http\Controllers\Pay"
    24. }
    25. ]

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

    1. if(is_null($constructor)) {
    2. return new $concrete;
    3. }

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

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

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

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

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

     return $reflector->newInstanceArgs($instances);
    

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

  • 相关阅读:
    Servlet 需要提供对应的doGet() 与 doPost()方法
    CCRC信息安全服务资质分类及申请流程
    硬件探索——模拟乘法器的综合应用设计实验
    Codeforces Round 952 (Div. 4)(实时更新)
    Keras计算机视觉(二)
    TypeScript 学习笔记(四):装饰器与高级编程技巧
    [论文阅读] Center-based 3D Object Detection andTracking-CenterPoint
    kr第三阶段(二)32 位汇编
    [附源码]java毕业设计人力资源管理系统论文2022
    基于JSP网上书城的设计与实现
  • 原文地址:https://blog.csdn.net/qq_20869933/article/details/132947585