Laravel框架下容器Container 的依赖注入和反射应用
依赖注入,简单说是把类里头依赖的对象,置于类外头,即客户端调用处。相当于把类与类解耦。
一个简单的例子:
class A { public function __construct() { // 这种实例化写法, A类的内部,依赖了B类 需要解耦 $this->b = new B(); } public function say(){ $this->b->talk(); } } class B{ public function __construct() { } public function talk(){ echo __METHOD__; } } $a = new A; $a->say();
A类依赖B类,耦合较重。
换一种写法:
class A { public function __construct(B $b) {// B的实例化,放在客户端,当作构造参数传递过来 $this->b = $b; } public function say(){ $this->b->talk(); } } class B{ public function __construct() { } public function talk(){ echo __METHOD__; } } $b = new B;// 在客户端实例化B $a = new A($b);// 构造器中传递 $a->say();
这个代码,就相当于实例化B的步骤放在了A类的外部,从而实现解耦。这就是依赖注入的一种实现方式。
Laravel框架中,通过这种依赖注入的方式,再配合反射功能,实现功能。
比如在Laravel的容器类,Container.php(vendor/laravel/framework/src/illuminate/Container/Container.php)中
利用反射,实例化注入的类。
来一段代码:
/** * * 依赖注入的思想,把外部依赖的类放在构造函数的参数里 * * Laravel框架根据反射 搞定依赖 */ class Test{ public function __construct(Para $p1, Parb $p2) { echo $p1->getInfo()," ",$p2->getInfo()," "; } function say(){ echo __CLASS__; return; } } /** * Class Para * 参数A */ class Para { function getInfo() { echo __CLASS__; } } /** * Class Parb * 参数B */ class Parb { function getInfo(){ echo __CLASS__; } } $obj = new Test(new Para(), new Parb()); // =================================================== $reflector = new ReflectionClass($obj);// 对象 反射其类信息 $constructor = $reflector->getConstructor(); $dependencies = $constructor->getParameters();// 获取构造器下的参数信息 $parArr = array(); foreach ($dependencies as $depend){ // $depend->getClass()->name 获取类名称 构造器参数是类 $parArr[] = new ($depend->getClass()->name)(); } $refNew = $reflector->newInstanceArgs($parArr);// Test 对象 $refNew->say();
代码中,获取$obj反射后的类信息,再取其构造器中的依赖类,实例化这些依赖类,再传入Test类中。
这就是Laravel容器中实现的依赖注入和反射应用。
下边的代码有助于理解Laravel框架下的容器Container概念。参考自文章:https://www.insp.top/article/learn-laravel-container
<?php interface SuperModuleInterface{ public function activate(array $target); } class Superman { protected $module; /** * Superman constructor. * @param SuperModuleInterface $module * 通过构造器 注入依赖 */ public function __construct(SuperModuleInterface $module) { $this->module = $module; } public function show(array $target){ $this->module->activate($target); } } class PowerA implements SuperModuleInterface { public function activate(array $target) { echo '<pre>'. __METHOD__; print_r(func_get_args()); } } class PowerB implements SuperModuleInterface { public function activate(array $target) { echo '<pre>'. __METHOD__; print_r(func_get_args()); } } class Container { protected $binds; protected $instances; /** * @param $abstract * @param $concrete * 把代词 绑定到容器里,为后续make */ public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } /** * @param $abstract * @param array $parameters * @return mixed * 创建对象 */ public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } // 把容器对象$this,放到参数数组第一个元素。为call_user_func_array使用 array_unshift($parameters, $this); // 这里$this->binds[$abstract] 绑定的闭包函数, 执行函数参数是$parameters return call_user_func_array($this->binds[$abstract], $parameters); } } // 创建一个容器(后面称作超级工厂) $container = new Container; // 向该 超级工厂添加超能力模组的生产脚本 $container->bind('powerA', function($container) { return new PowerA; }); // 同上 $container->bind('powerB', function($container) { return new PowerB; }); // 向该 超级工厂添加超人的生产脚本 $container->bind('superman', function($container, $moduleName) { return new Superman($container->make($moduleName)); }); echo "<pre>"; // 开始启动生产 $superman_1 = $container->make('superman', ['powerA']); $superman_1->show(['a' => 1]); $superman_2 = $container->make('superman', ['powerB']); $superman_2->show(['b' => 1]);