Yii框架依赖注入DI容器

本文介绍了Yii框架中的依赖注入(Dependency Injection, DI)和控制反转(Inversion of Control, IOC)概念,解释了DI容器如何管理应用程序全局对象并自动维护对象间的依赖关系。通过代码演示,阐述了为什么要使用控制反转,以及如何实现控制反转和依赖注入,最后提到了Yii2框架DI容器的具体实现和进一步学习资源。" 126822521,15215129,Java民航售票管理系统毕业设计源码及文档,"['Java开发', 'Mybatis框架', 'B/S系统', '数据库管理', '前端开发']
摘要由CSDN通过智能技术生成

背景:前几天在segmentfault花了两个小时学习了《自己动手造轮子, 实现一个现代的PHP框架》,当然这个课程只能是给学习者提供一个思路,比如说一个框架应该由哪些组件组成,然后再大致讲解下实现每个组件的思路。课程中涉及到依赖注入容器的实现,我之前并没有研究过,所以上课时简直一脸懵圈,正好最近在学习Yii框架,现在就从Yii框架-依赖注入(DI)容器-开始吧!

基本概念


依赖注入(Dependency Injection)
  • 降低了依赖与被依赖类型之间的耦合
  • 可以单独维护被依赖类型的创建过程
控制反转(Inversion of Control)
  • 转移依赖关系 降低耦合
  • 依赖抽象类型而非具体类型
依赖注入容器(Dependency Injection Container)
  • 管理应用程序中的全局对象
  • 自动维护对象之间的依赖关系 (自动注入依赖)
  • 可以延迟加载对象(仅用到时才创建对象)。
  • 启动容器后,所有对象直接取用

其实我觉得这个依赖注入和控制反转其实是同一个东西,只不过所强调有所不同,IOC强调的是一种降低依赖与被依赖类型之间耦合的模式,而依赖注入则是强调注入这个动作,把被依赖对象从程序中剥离出来,使用动态的注入方式。

代码演示


没有代码的说明,可能对上述概念还会比较模糊,下面先引入问题,再尝试用上述的概念指导改进代码的实现

为什么需要控制反转

假设应用程序有发送短信的需求,如果使用耦合度最高方式,可能是这样的

class Register
{
   
    public $sender;
    public function __construct()
    {
   
        $this->sender = new Alidayu();
    }
    public function sendMessage($phone,$content)
    {
   
        $this->sender->send($phone,$content);
    }
}
class Alidayu
{
   
    public function send($phone,$content)
    {
   
        //发送短信
    }
}

$register = new Register();
$register->sendMessage('13312341234','注册成功');

当我们不使用阿里大于,而改用其他第三方短信服务时,那我们还需要对Register代码进行改动。显然,Alidayu和Register之间耦合度很高。

控制反转代码实现

依据控制反转的概念,我们在Register中的依赖应当基于抽象,而非基于具体的实现,对上述代码做出修改

Interface Sender
{
    public function send($phone,$content);
}
class yunliantong implements Sender
{
   
    public function send($phone,$content)
    {
        //发送短信
    }
}
class Register()
{
   
    public $sender;
    public function __construct(Sender $sender)
    {
        $this->sender = $sender;
    }
}
$sender   = new yunliantong();
$register = new Register($sender);
$register->sender->send('1331234567','注册成功');

这就是IOC控制反转,从原来的依赖具体类型,到现在的依赖抽象类型,把被依赖的类型解放出来,允许被依赖的类型独立变化,使用的时候再动态的注入依赖的对象中。

现在,你仔细分析,这个控制反转是和依赖注入是否是同一个东西,实现这个控制翻转,是通过依赖注入这个动作来完成的


现在看来,上述代码似乎并没有什么太大问题了。
但如果这个Register依赖的对象有多个,同时这些依赖的对象又依赖了其他的对象,这样的话,代码有可能是下面这样的。

$db     = new db();
$user   = new user($db);
$Mail   = new Mail();
$Mailer = new qqMailer($Mail);
$sender = new yunliantong();
...
如果还有更多的话 。。。
...
$register = new Register($sender,$Mailer,$user);

这样手动的为对象注入依赖,会带来许多不便,因为我们必需要注意创建对象的顺序,那能不能做到自动的注入依赖?注意,是自动,你想到了什么?
对,就是递归地创建依赖的对象。

比如说A依赖B,B依赖C,C无依赖。
现在我直接取出A的实例,系统自动帮我创建好它们之间的依赖关系,流程应该是这样的。
这里写图片描述

那系统怎么知道A有没有依赖,如果有依赖,那又怎么知道是依赖何方神圣?答案是使用PHP5 的反射机制,详情见php-manual-reflection

反射是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。
其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言。

为了配合下面的yii DI容器的源码解读,对将使用的类方法进行简单的说明。


使用ReflectionClass返回一个类的内部信息

$reflector = new ReflectionClass('类名称');

使用getConstructor( ) 获取类的构造函数

$constructor = $reflector->getConstrutor();

使用getParameters( ) 获取函数参数

$params = $constructor->getParameters();

使用isDefaultValueAvailable( ) 判断参数是否有默认值
使用getDefaultValue( )获取默认值
使用getClass( )获取参数的暗示的类

foreach($params as $param)
{
    if ($param->isDefaultValueAvaliable()) {
        echo $param->getDefaultValue();
    }else{
        echo $param->getClass();
    }
}

使用newInstanceArgs 从指定的参数生成类的新实例.

class A
{
   
    public $name;
    public function __construct($name)
    {
   
        $this->name = $name;
    }
}
$reflector = new ReflectionClass('A');
$object = $reflector->newInstanceArgs(['I AM A']);
print_r($object);//Object ( [name] => I AM A )

小试牛刀

实现用递归注入依赖信息

class simpleDI
{
   
    //依赖
    public $_dependencies = [];
    //反射
    public $_reflection = [];

    //获取一个实例
    public function get($class)
    {
   
        //如果是一个类就实例化
        if(class_exists($class)) {
            return $this->build($class);
        }
        throw new 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值