网上 对容器的解释有很多,这里只是记录,搬运!

1、简单理解:

  

2019-10-10 11:24:09
解析 lavarel 容器
利用 IoC 将你设计好的对象交给容器控制,而非传统地在你的对象内直接控制、处理。
IoC 容器 作用 就是 “解耦” 、“依赖注入(DI) IoC 容器,根据文档,称其为 “服务容器” 主要存放 是对象、对象的描述(类、接口)或者是提供对象的回调 面向对象开发中依赖的产生和解决方法 IoC(控制反转) 和 DI(依赖注入) 面向对象编程,有以下几样东西无时不刻的接触:接口、类还有对象 这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例 工厂模式,顾名思义,就是一个类所以依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。 只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 更为先进的工厂 —— IoC 容器 可以说,laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 laravel 的服务容器负责的 对,一个类要被容器所能够提取,必须要先注册至这个容器。既然 laravel 称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是 服务提供者(ServiceProvider) 服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档。register 负责进行向容器注册 “脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分。 们使用的 Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的 别名 而已,这个类被定义在文件 vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php 。 bind 就是 实例化 make 传参 Contracts(契约),也就是接口。定义一些方法,所以实现此接口的类都要实现契约里边的方法。
ServiceContainer (契约的具体实现) ,实现 Contracts。具体实现逻辑写在这里。 ServiceProvider (服务提供者) ,绑定事务到服务容器。 App (服务容器), 在服务提供者中,可以通过 $this->app或App:: 得到。 App::其实就是门面。 Facades(门面), 简化服务提供者的调用方式,用静态的方式调用具体实现里的方法。 Ioc全称Inversion of Control,意为控制反转 https://learnku.com/articles/789/laravel-learning-notes-the-magic-of-the-service-container

  

2、代码简单理解

  2.1

Interface Power
{
public function fight();
} class FlyPower implements Power
{
public function fight()
{
echo "我是飛行能力" . PHP_EOL;
}
} class FirePower implements Power
{
public function fight()
{
echo "我是開火能力" . PHP_EOL;
}
} class XrayPower implements Power
{
public function fight()
{
echo "我是x光線能力" . PHP_EOL;
}
} class Superman
{
private $power; public function __construct(Power $power)
{
$this->power = $power;
} public function go()
{
$this->power->fight();
}
} $power1 = new FlyPower();
$power2 = new FirePower();
$power3 = new XrayPower();
$superman1 = new Superman($power1);
$superman1->go();
$superman2 = new Superman($power2);
$superman2->go();
$superman3 = new Superman($power3);
$superman3->go();

在这个例子中,SuperMan的构造方法需要一个参数,该参数必须实现Power接口,而这个参数是我们在类的外部进行实例化后传入的(当作参数),只要是Power的实现就都有效,所以相当于 把Power注入到Superman中,即依赖注入

2.2 容器:依据简单的理解,做的简单的容器,这个容器 需要自己绑定,自己注入,而lavarel 实现的的反向依赖注入,自动注入

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();
// bind方法:第一個參數可以理解爲要綁定的key,別名,第二個參數可以是一個回調函數,也可以是一個實例對象
$container->bind('flypower', function ($container) {
return new FlyPower();
});
// bind的第二個參數爲回調函數的情況
$container->bind('firepower', new FirePower());
// bind的第二個參數爲實例對象的情況
$container->bind('superman', function ($container, $power) {
return new Superman($container->make($power));
}); $superman1 = $container->make('superman', ['flypower']);
$superman2 = $container->make('superman', ['firepower']);
$superman1->go();// 輸出:我是飛行能力
$superman2->go();// 輸出:我是開火能力

2.3 模拟lavarel 是实现的自动 反向依赖注入:

   

  • php反射用法

  • 闭包的use用法

class Container
{
protected $bindings = []; public function bind($abstract, $concrete = null, $shared = false)
{
if (!$concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = ['concrete' => $concrete, 'shared' => $shared];
} protected function getClosure($abstract, $concrete)
{
/*
* use 的使用
*/
return function ($c) use ($abstract, $concrete) {
$method = ($abstract == $concrete) ? "build" : "make";
return $c->$method($concrete);
};
} public function make($abstract)
{
//這裏的註釋在調試時,建議打開,可以直觀的跟蹤程序的執行順序和流程
//static $i = 1;
//echo "make-".$i++.PHP_EOL; $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
return $object;
} protected function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
} protected function getConcrete($abstract)
{
if (!isset($this->bindings[$abstract])) {
return $abstract;
}
return $this->bindings[$abstract]['concrete'];
} public function build($concrete)
{
//這裏的註釋在調試時,建議打開,可以直觀的跟蹤程序的執行順序和流程
//static $i = 1;
//echo "build-".$i++.PHP_EOL; if ($concrete instanceof Closure) {
return $concrete($this);
}
/*
* 反向依赖
*/
$ref = new ReflectionClass($concrete); if (!$ref->isInstantiable()) {
echo "The $concrete is not instantiable.";
exit;
}
$constructor = $ref->getConstructor();
if (is_null($constructor)) {
return new $concrete;
}
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);
return $ref->newInstanceArgs($instances);
} protected function getDependencies($parameters)
{
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (is_null($dependency)) {
$dependencies[] = null;
} else {
$dependencies[] = $this->resolveClass($parameter);
}
} return (array)$dependencies; } protected function resolveClass(ReflectionParameter $parameter)
{
return $this->make($parameter->getClass()->name);
}
} $c = new Container();
$c->bind("Power", "FlyPower");
$c->bind("superman1", "Superman");
$superman1 = $c->make("superman1");
//$c->bind("Superman","Superman");
//$superman1 = $c->make("Superman");
$superman1->go(); //輸出:我是飛行能力

  

3、代码扩展

   3.1 多个db接口的扩展

    

//数据库接口
interface Sql
{
public function query();
} class Mysql implements Sql { public function __construct(){} public function query()
{
echo "Mysql is working!\n";
}
} class Postgresql implements Sql { public function __construct(){} public function query()
{
echo "Postgresql is working!\n";
}
} class MSsql{ public function query()
{
echo "MSsql is working!\n";
}
} class doQuery{
protected $dosql; public function __construct(Sql $sql, A $a)
{
$this->dosql = $sql;
$this->a = $a;
} public function query()
{ $this->a->do();
$this->dosql->query();
}
} class A{
public function do()
{
echo "A works!\n";
}
} $app = new Container();
// Sql的实现,也就是concrete为Postgresql
$app->bind("Sql", "Postgresql");
// myQuery是abstract,可以当作别名,而doQuery是其实现
$app->bind("myQuery", "doQuery");
$app->bind("closure", function($c){
echo "closure works!\n";
});
echo "\n\n";
$app->make("closure");
echo "\n\n";
$app->make("A")->do();
echo "\n\n";
// make的过程:
// 1. 由于创建的是myQuery,所以找到doQuery
// 2. 容器在初始化doQuery时发现他是闭包,于是执行$app->make("doQuery")
// 3. 回到make的getConcrete(),发现返回doQuery,因为没有doQuery对应的bind
// 4. isBuildable()发现$concrete === $abstract,于是可build
// 5. 进入build流程,跳过闭包检测,开始执行反射
// 6. 如果没有构造函数,直接实例化,如果有,解析出依赖
// 7. 拿出一个依赖,如果不为空,进入依赖解析环节,此时发现doQuery依赖$db
// 8. $db实现了Sql的接口,于是调用$app->make(Sql),而SQL在初始化的时候被bind到了Postgresql上
// 9. 回到3
$myQuery = $app->make("myQuery");
echo "\n\n";
$myQuery->query();
echo "\n\n";
$app->bind("Sql", "Mysql");
$myQuery = $app->make("myQuery");
$myQuery->query();
echo "\n\n";
// MSsql没有依赖,getConcrete返回MSsql,然后实例化
$myQuery = $app->make("MSsql");
$myQuery->query();

4、lavarel 部分代码解析

    

简单的绑定

$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});

单例模式绑定


通过 singleton 方法绑定到服务容器的类或接口,只会被解析一次。

 
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
也可以通过 instance 方法把具体的实例绑定到服务容器中。之后,就会一直返回这个绑定的实例:
$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);
 

相关链接:

Laravel 学习笔记 —— 神奇的服务容器

php di

http://php-di.org/doc/getting-started.html

Laravel - 核心概念 - 服务容器(IoC) - 绑定

https://www.yy-tao.com/detail/208.html

https://www.cntofu.com/book/107/Laravel%20Container%E2%80%94%E2%80%94IoC%20%E6%9C%8D%E5%8A%A1%E5%AE%B9%E5%99%A8.md

laravel5.8 IoC 容器的更多相关文章

  1. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  2. IL实现简单的IOC容器

    既然了解了IL的接口和动态类之间的知识,何不使用进来项目实验一下呢?而第一反应就是想到了平时经常说的IOC容器,在园子里搜索了一下也有这类型的文章http://www.cnblogs.com/kkll ...

  3. 通过中看不中用的代码分析Ioc容器,依赖注入....

    /** * 通过生产拥有超能力的超人实例 来理解IOC容器 */ //超能力模组接口 interface SuperModuleInterface{ public function activate( ...

  4. Spring在非web应用中关闭IoC容器 (registerShutdownHook)

    在基于web的ApplicationContext实现中,已有相应的实现来处理关闭web应用时恰当地关闭Spring IoC容器.但,如果你正在一个非web应用的环境下使用Spring的IoC容器,如 ...

  5. .net自带的IOC容器MEF使用

    IOC能做什么 IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序. 控制反转: 将控制权移交给第三方容器  new 操作 依赖注入: 在程序 ...

  6. NET 自带IOC容器MEF指初体验

    转自:http://www.cnblogs.com/ulex/p/4186881.html IOC容器:工具较多,大体功能都相同,大都需要事先对接口与实现进行配对(通过代码或配置文件),然后由系统自动 ...

  7. 自定义模拟一个Spring IOC容器

    一.模拟一个IOC容器: 介绍:现在,我们准备使用一个java project来模拟一个spring的IOC容器创建对象的方法,也就是不使用spring的jar自动帮助我们创建对象,而是通过自己手动书 ...

  8. IoC原理-使用反射/Emit来实现一个最简单的IoC容器

    从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...

  9. Spring框架IOC容器和AOP解析

    主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.S ...

随机推荐

  1. Linux 查看Tomcat版本信息

    Linux 查看Tomcat版本信息 如果我们想运行在 Linux 下的 Tomcat 版本信息,只需要在 Tomcat 的 bin/ 目录下,运行 version.sh 脚本即可. 1.使用如下命令 ...

  2. MATLAB学习(三)元素访问和常用代数运算

    >> A=[1,2;3,4],B=[0,2;4,5] A = 1 2 3 4 B = 0 2 4 5 >> C=A>=B C = 1 1 0 0 >> D=A ...

  3. EM算法:入门案例

    概率分布 4种实验结果 \(E_1\) \(E_2\) \(E_3\) \(E_4\) 记录它们发生的次数 \(y_1\) \(y_2\) \(y_3\) \(y_4\) 记录次数结果 125 18 ...

  4. SqlServer:SqlServer(服务器磁盘监控,创建管理员账号分配权,添加链接服务器,查询CPU,查询内存)

    1.服务器磁盘监控 (1)总链接服务上开启所有链接服务器的RPC: ----------------------总链接服务器上面,开启每个服务器的RPC --exec sp_serveroption ...

  5. python-Web-django-商城-购物车商品加减

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. TPM大端模式

    1. Big-Endian(BE)大端模式 数据是按照,“高字节.低存储”,即高字节存储在低地址,符合人们直观感受 2. Little-Endian(LE)小端模式 数据是按照,“低字节,低存储”,即 ...

  7. 菜鸟系列docker——搭建私有仓库harbor(6)

    docker 搭建私有仓库harbor 1. 准备条件 安装docker sudo yum update sudo yum install -y yum-utils device-mapper-per ...

  8. Spring自定义参数解析器

    结合redis编写User自定义参数解析器UserArgumentResolver import javax.servlet.http.Cookie; import javax.servlet.htt ...

  9. PHP中的闭包

    1.语句结构 在PHP中,由于存在函数内部不能访问全局作用的,所以就需要一种可以引入上一级作用域的语法结构,这种就是 function () use () {} 将需要引入到这个函数作用于内的变量写入 ...

  10. LeetCode 19——删除链表的倒数第N个节点(JAVA)

    给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 ...