Laravel中的ioc容器和反射机制

那么,我们今天要说的服务容器就是为了实现上面的功能.你应该听过,Laravel中的服务容器其本质上是一个IoC容器,但是好像队IoC又不是很了解,讲来讲去优点很多,功能很强劲.但是不懂原理怎么用都不踏实啊.所以,这里我们自己来实现一个IoC容器,洞察其本质.

在开始之前,先说明一点,阅读本篇文章至少要保证有一下的基础知识:

php反射用法

闭包的use用法

如果不懂上面的内容,请先补充.避免阅读代码时候产生的不适感.

<?php


class Container
{
    public $binding = [];


/**
 * @param $abstract
 * @param null $concrete
 * @param bool $shared  
 */
public function bind($abstract, $concrete = null, $shared = false)
{
    if (!$concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete);
    }

    $this->binding[$abstract] = compact('concrete', 'shared');
}



protected function getClosure($abstract, $concrete)
{
    return function ($c) use ($abstract, $concrete) {
        $method = ($abstract == $concrete) ? 'build' : 'make';
        return $c->$method($concrete);
    };
}

/**
 * @param $abstract
 * @return object
 *
 */
public function make($abstract)
{
    $concrete = $this->getConcrete($abstract);

    if ($this->isBuildable($concrete, $abstract)) {
        $object = $this->build($concrete);
    } else {
        $object = $this->make($concrete);
    }
    return $object;
}

/**
 * @param $concrete
 * @param $abstract
 * @return bool
 */
public function isBuildable($concrete, $abstract)
{
    return $concrete === $abstract || $concrete instanceof Closure;
}

/**
 * @param $abstract
 * @return mixed
 */
protected function getConcrete($abstract)
{
    if (!isset($this->binding[$abstract])) {
        return $abstract;
    }
    return $this->binding[$abstract]['concrete'];
}


/**
 * @param $concrete
 * @return object
 */
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);
}

/**
 * @param $paramters
 * @return array
 * 获取构建类所需要的所有依赖,级构造函数所需要的参数 ,
 */
protected function getDependencies($paramters) {
    $dependencies = [];

    foreach ($paramters as $paramter) {
        //获取到参数名称.
        $dep = $paramter->getClass();
        if(is_null($dep)){
            $dependencies = null;
        }else{
            $dependencies[] = $this->resolveClass($paramter);
        }
    }
    return (array)$dependencies;
}

/**
 * @param ReflectionParameter $parameter
 * @return object
 * 实例化 构造函数中所需要的参数.
 */
protected function resolveClass(ReflectionParameter $parameter) {
    $name = $parameter->getClass()->name;
    return $this->make($name);
 }

}

这就是一个IoC容器的实现代码.乍一看,很麻烦.其实真的蛮麻烦的 =_=,如果是第一次接触的话,并不是那么好消化,这里再给出使用IoC容器的代码

<?php

require __DIR__ . '/Container.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();
  }
}

//实例化IoC容器
$app = new Container();

//绑定某一功能到IoC
$app->bind('TrafficTool', 'Train');
$app->bind('travellerA', 'Traveller');

// 实例化对象
$tra = $app->make('travellerA');
$tra->visitTibet();

运行例子发现会输出: train… .

这个例子假设旅行者去青藏旅行,可以坐火车(train)或者走路(leg)去青藏.

首先,希望你可以运行一下上面的代码,虽然简单的运行代码并不会帮助你理解代码,但是一个可以运行的例子会让人比较踏实,能够更有把握的理解代码.

在深入每一行代码之前,我们从整体上来分析,IoC解决了一个什么问题?简单点说,就是我们再实例化对象的时候不用使用new了,有了IoC容器之后,我们调用make函数就可以实例化出一个对象了.然而,你发现,Traveller的构造函数是需要一个参数的,可是我们好像并没有提供这个参数?

这就是IoC强大之处了, 调用make实例化对象的时候,容器会使用 反射 功能,去分析我们要实例化对象的构造函数,获取构造函数所需的每个参数,然后分别去实例化这些参数,如果实例化这些参数也要参数,那么就再去实例化参数的参数……=_=.到最后成功实例化我们所需要的traveller了.在Container的build函数就是使用反射来实例化对象.

但是,有一个问题了,IoC容器怎么知道实例化Traveller的时候需要的参数train,而不是leg?

其实,IoC容器什么都不知道,IoC会实例化哪些对象都是通过 bind 函数告诉IoC的,上面的例子两次调用bind函数,就是告诉Ioc可以实例化的对象有 Train 和 Traveller . 再通俗讲就是:当需要当我们需要 TrafficTool 这个服务的时候去实例化 Train 这个类,需要一个 travellerA 的旅行者的时候去实例化 Traveller 类.而 Train 这个就是 travellerA 就是去青藏的方式. 这样子如果想要走路去青藏的话只要把 $app->bind(‘Visit’, ‘Train’); 改为 $app->bind(‘Visit’, ‘Leg’); 就可以.

可是,这上面的这些有什么意义?直接 t r a = n e w T r a v e l l e r ( tra = new Traveller( tra=newTraveller(trafficTool) 来实例化对象好像也没有什么不好的.

使用new来实例化对象的时候,会产生依赖.比如上面 t r a = n e w T r a v e l l e r ( tra = new Traveller( tra=newTraveller(trafficTool) ,这说明我们要创建一个Traveller之前得有一个 $trafficTool ,即 Traveller 依赖于 trafficTool .当使用new来实例化 Traveller 的时候, Traveller 和 trafficTool 之间就产生了 耦合 .这样,这两个组件就没办法分开了.

而使用IoC是怎么解决这个问题的,之前说过,如果想要如果想要走路去青藏的话只要把 $app->bind(‘Visit’, ‘Train’); 改为 $app->bind(‘Visit’, ‘Leg’); 就可以.这样子,使用何种方式去青藏,我们可以自由的选择.

我们站在Laravel框架设计者的角度去想,设计者肯定希望一个框架提供的功能越多越好,但是又要保证强大的同时又不会限制使用者.最好可以保证使用者想实现什么奇怪的需求都可以.那么功能强大但是又不局限的最好方法就是什么都不做,提供一个强大的IoC容器.所有需要实现的功能都变成一个个服务,需要什么服务就把服务注册(即调用bind函数)到IoC中,然后让IoC去管理依赖.

开发者想到一个变态的需求:走路去青藏,那么只要你实现了走路去青藏这个功能,然后把这个功能当做一个服务注册到IoC中,以后你需要这个服务的时候IoC就帮你实例化这个服务.当开发者回归正常之后觉得还是坐火车去吧,于是不注册走路这个功能,实现坐火车的功能,然后注册这个功能.下次IoC实例化的时候就是实例化坐火车这个功能了.

好了,剩下的部分就是一行一行的阅读Container的代码了,Laravel框架中的服务容器代码也是这个样子,只是功能更加强悍.但是核心是一样的,上面的代码懂了以后再使用Laravel框架就会更加游刃有余了.

文章虽短.但是内容很多.尤其是代码,虽然可能只是短短的一个例子,但是包含了很多内容.值得好好分析,这里放个彩蛋:Traveller中构造函数参数类似为TrafficTool,是一个接口.但是实例化的是Train.这里体现了设计模式的一个原则

面对接口编程,而不是面对实现编程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring的IOC容器运行机制是通过配置元数据的加载、Bean的实例化、依赖注入、生命周期管理和AOP支持等机制来实现对Bean的管理和控制。具体来说,首先需要定义一个或多个XML配置文件,其包含了对应的Bean定义和它们之间的关系。然后,在应用程序启动时,IOC容器会读取这些配置文件,并根据配置信息创建和管理相应的Bean实例。IOC容器会负责将依赖关系注入到Bean,即根据配置信息,自动将依赖的对象注入到需要使用它们的对象,从而降低了组件之间的耦合度。此外,IOC容器还负责管理Bean的生命周期,包括初始化和销毁。最后,IOC容器还提供了AOP(面向切面编程)的支持,通过代理机制,可以在不修改原有代码的情况下,为Bean添加额外的功能。总的来说,通过这些机制,Spring的IOC容器实现了对Bean的管理和控制,提高了代码的可维护性和可测试性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring详细学习资料下载](https://download.csdn.net/download/xs765914759/83322672)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Spring题集 - Spring IoC容器相关面试题总结](https://blog.csdn.net/qq_42764468/article/details/129468636)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值