思考并回答以下问题:
- 依赖注入为什么又叫控制反转?
- 只要知道类的名字就能实例化该类。依赖注入就是传类名做参数,然后保证有这个类就好了。怎么理解?
- 手动和自动依赖注入的区别是什么?
- PHP支持可变长度参数吗?
使用
1 | class A |
测试构造函数的依赖注入
1 | // 使用IOC来创建A类的实例,A的构造函数依赖B类,B的构造函数依赖C类。 |
测试方法依赖注入
1 | $methodResult = IOC::make('A', 'aa', ['this is param a']); |
从上面两个例子可以看出我们创建对象或者调用方法时,根本就不用知道该类或该方法依赖了哪个类。使用反射机制可以轻松的为我们自动注入所需要的类。
源码
使用php的反射函数,创建一个容器类,使用该类来实现其他类的依赖注入功能。依赖注入分为两种,一种是构造函数的依赖注入,一种是方法的依赖注入。
1 | /** |
laravel中利用反射实现依赖注入
在一个类中经常会依赖于其他的对象,先看一下经典的写法。
1 | class Foo |
当类的依赖发生改变时,比如Bar这个类需要实例化参数时,而依赖于它的类有很多,总不能一个一个地去修改吧。
再看一下使用依赖注入怎么做、
1 | class Foo |
将Bar类在外部实例化好后,作为一个参数传入进Foo类,从而实现了控制反转,假如现在Bar类需要参数了,外部修改就好了,不必一个个地去修改依赖于它的类。
在laravel中,经常写出下面这种代码:
1 | class SomeController |
只要在方法参数中申明Request $request,就可以直接使用$request对象了,非常地方便。
其实laravel在背后利用PHP的反射机制为我们做了$request = new Request这一步。反射是一种类的反省能力,可以导出类的详细信息包括属性、方法、甚至注释等等。
实现,看代码:1
2
3
4
5
6
7
8
9
10
11$method = new ReflectionMethod('SomeController', 'index');
$args = [];
foreach($method->getParameters() as $parameter)
{
if ($class = $parameter->getClass())
{
$args[] = new $class->name; //$request = new Request
}
}
$method->invokeArgs(new SomeController, $args);
通过ReflectionMethod获取类方法的参数,如果参数是其他的类,就实例化后作为参数使用ReflectionMethod::invokeArgs 传入到类方法中,原理就是这么简单。
通常使用new ReflectionClass(‘className’) 来反射类,ReflectionMethod来反射类方法。
总结
只要熟悉php的反射机制,依赖注入并不难实现,上面的代码为了方便理解,所以写的简单粗暴,在实际的项目中肯定不会这么简单,比如:会对注入的类和参数进行配置,比如会缓存实例化过的类,下次需要该类的实例时,可以直接使用,而不用在重新初始化,等等。不过相信原理了解了,其他的可以随着项目的需求自己去完善。