初识依赖注入和Ioc容器
来源:互联网 发布:淘宝分销是什么意思 编辑:程序博客网 时间:2024/05/23 19:25
初始写法
当A类使用B类,最开始的写法是:在A类内部新建B类的对象,然后使用。
例如,现在有个控制器类,需要从Repository类来获取数据。原始写法:
<?phpclass Repository{ public function getData(){ return "data"; }}class Controller{ private $repository; public function __construct(){ $this->repository = new Repository(); } public function showData(){ $data = $this->repository->getData(); echo $data; }}$c = new Controller();$c->showData();
依赖注入
同样是A类使用B类:现在我们在外部创建B类的对象,然后传给A类。
现在A类不需要创建B类,只管调用B类的方法。
下面是从构造器注入的示例:
<?phpclass Repository{ public function getData(){ return "data"; }}class Controller{ private $repository; public function __construct(Repository $repository){ $this->repository = $repository; } public function showData(){ $data = $this->repository->getData(); echo $data; }}//在其他的某个地方,实例化并且注入$repository = new Repository();$c = new Controller($repository);$c->showData();
依赖反转
借鉴这篇文档:依赖反转准则
是指
依赖应该是接口/约定或者抽象类,而不是具体的实现。
示例:
<?php//首先定义一个获取数据的接口interface RepositoryInterface{ public function getData();}//一个具体的实现类,它从mysql数据库获取数据class fromMysqlRepository implements RepositoryInterface{ public function getData(){ return 'this is data from mysql database'; }}class Controller{ private $repository; //注入的依赖是一个接口 public function __construct(RepositoryInterface $repository){ $this->repository = $repository; } public function showData(){ $data = $this->repository->getData(); echo $data; }}//实例化,注入$repository = new fromMysqlRepository();$c = new Controller($repository);$c->showData();
某天,需要换一种获取数据的方式,比如从redis缓存中获取数据。这时候要修改的地方:
//新建一个类,也是实现RepositoryInterface接口,不过方法的具体实现是从redis获取数据。class fromRedisRepository implements RepositoryInterface{ public function getData(){ return 'this is data from redis'; }}$repository = new fromRedisRepository();//实例化的地方,只需要修改这处代码$c = new Controller($repository);$c->showData();
一个初级的Ioc容器
上面的例子,都是需要手动创建依赖、注入。 创建依赖和注入,可以交给Ioc容器去做。
IOC控制反转:即
“创建对象实例的控制权从代码控制剥离到IOC容器控制。”
一个示例(来源于这里:https://www.insp.top/article/learn-laravel-container。):
interface RepositoryInterface{ public function getData();}class fromMysqlRepository implements RepositoryInterface{ public function getData(){ return 'this is data from mysql database'; }}class fromRedisRepository implements RepositoryInterface{ public function getData(){ return 'this is data from redis'; }}class Controller{ private $repository; public function __construct(RepositoryInterface $repository){ $this->repository = $repository; } public function showData(){ $data = $this->repository->getData(); echo $data;echo '<br>'; }}//手动地创建依赖,并注入。现在要改变这个做法//$repository = new fromMysqlRepository();//$repository = new fromRedisRepository();//$c = new Controller($repository);//$c->showData();//一个初级的容器class Container{ protected $binds; protected $instances; public function bind($abstract, $concrete){ if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []){ if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); }}// 创建容器$container = new Container;// 向容器添加Controller的创建脚本$container->bind('controller', function($container, $moduleName) { return new Controller($container->make($moduleName));});// 向容器添加fromMysqlRepository的创建脚本$container->bind('mysql', function($container) { return new fromMysqlRepository;});// 同上$container->bind('redis', function($container) { return new fromRedisRepository;});//在某处创建、调用。$c1 = $container->make('controller',['mysql']);$c1->showData();
解读
这个容器的例子来源于这里:https://www.insp.top/article/learn-laravel-container。
我读到这个例子时,看了好一会才看懂。 已经看懂的请忽略这个“解读”
当$container->make('controller',['mysql']);
的时候,发生了什么事?
1、第一次进入容器的make方法: array_unshift($parameters, $this);
重新组装了$parameters,有2个元素:第一个是容器,第二个是字符串’mysql’。
call_user_func_array($this->binds[$abstract], $parameters);
此时 $this->binds[$abstract]
是刚才绑定的一个闭包controller
,也就是这样的内容:
function($container, $moduleName) { return new Controller($container->make($moduleName));}
2、 controller
这个闭包再次调用make
。第二次进入容器的make方法: array_unshift($parameters, $this);
此时传进来的$parameters
是空数组,重新组装后,只有一个元素,也就是容器
call_user_func_array($this->binds[$abstract], $parameters);
此时 $this->binds[$abstract]
是另一个闭包 ‘mysql’,内容是
function($container) { return new fromMysqlRepository;};
3、所以,$container->make('controller',['mysql']);
这一句代码
做了2件事,先新建一个fromMysqlRepository对象;再新建一个controller对象,并且向它注入了上一步新建的fromMysqlRepository对象.
在Ioc容器中使用反射
laravel5.5 的容器位于vendor\laravel\framework\src\Illuminate\Container\Container.php
。
larave的Ioc容器中使用到了反射。 主要思路是通过反射获取对象的构造函数,进而获取构造函数参数,根据参数创建依赖,然后创建对象。
主要代码:
public function build($concrete){ if ($concrete instanceof Closure) { /* ...... */ } $reflector = new ReflectionClass($concrete); // 获得反射的对象 if (!$reflector->isInstantiable()) { /* ...... */ } $this->buildStack[] = $concrete;// 获取构造器 $constructor = $reflector->getConstructor(); if (is_null($constructor)) { /* ...... */ } $dependencies = $constructor->getParameters(); // 获取构造器参数(一组ReflectionParameter 对象) $instances = $this->resolveDependencies($dependencies); // 创建依赖 array_pop($this->buildStack); return $reflector->newInstanceArgs($instances);// 利用依赖创建对象}protected function resolveDependencies(array $dependencies){ $results = []; foreach ($dependencies as $dependency) { if ($this->hasParameterOverride($dependency)) { /* ...依赖如果被重写过的处理... */ } $results[] = is_null($dependency->getClass()) ? $this->resolvePrimitive($dependency) //如果不是一个可实例化的类,就“bomb out with an error” : $this->resolveClass($dependency); //没问题,就用make()创建依赖的实例 } return $results;}protected function resolveClass(ReflectionParameter $parameter){ try { return $this->make($parameter->getClass()->name); } catch (BindingResolutionException $e) { /* ...处理... */ throw $e; }}
- 初识依赖注入和Ioc容器
- IOC容器注入依赖
- IoC容器和依赖注入模式
- AspectCore中的IoC容器和依赖注入
- AspectCore中的IoC容器和依赖注入
- IOC容器的依赖注入
- Spring Ioc容器依赖注入
- IOC容器的依赖注入
- 依赖注入&控制反转 IoC 容器和Dependency Injection 模式
- 依赖项注入 (DI) 和控制反转 (IOC) 容器模型
- spring容器中的依赖注入和ioc那些事
- Spring容器的控制反转(IOC)和依赖注入(DI)
- 详解Laravel依赖注入(DI)和Ioc容器
- spring IOC容器的初始化和依赖注入
- 详解Laravel依赖注入(DI)和Ioc容器
- IoC容器与依赖注入DI模式
- Spring框架学习【IoC容器依赖注入】
- [IoC容器Unity]第三回:依赖注入
- STM32之中断管理
- 4种jdk自带的常用线程池简单介绍
- bzoj4530 [Bjoi2014]大融合 (LCT维护子树信息)
- objdump
- 计算机网络-001-计算机网络在信息时代中的作用
- 初识依赖注入和Ioc容器
- leetcode_27. Remove Element ? 待解决
- 第九周 项目3 利用二叉树遍历思想解决问题
- 浅谈压缩感知(一):背景简介
- Java synchronized关键字
- 使用函数添加环境变量
- Wannafly挑战赛4——A-解方程(二分)
- PHP中,Json AJax传输数据
- 120. Triangle