1. 介绍
laravel 容器
存放的 是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦”、“依赖注入(DI)”。
服务容器的使用
2. 通过案例解析IoC和DI
多个超人多种能力
我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。
IoC设计思想:将依赖关系动态注入
工厂模式,依赖转移
工厂模式的缺点就是:
接口未知(即没有一个很好的契约模型,关于这个我马上会有解释)、产生对象类型单一。
总之就是,还是不够灵活。
虽然如此,工厂模式依旧十分优秀,并且适用于绝大多数情况。
class Superman4{protected $power;public function __construct(array $modules){// 1 单个能力,依赖// $this->power = new Power(999, 100);// 2 多个能力,依赖$this->power = new Flight(9, 100);// $this->power = new Force(45);// $this->power = new Shot(99, 50, 2);$this->power = [new Force(45),new Shot(99, 50, 2),];// 3 多个能力,工厂模式(依赖)$factory = new SuperModuleFactory();// 和之前区别不大,但没有那么多new关键字$this->power[] = $factory->makeModule('Flight', [9, 100]);// $this->power = $factory->makeModule('Force', [45]);// $this->power = $factory->makeModule('Shot', [99, 50, 2]);$this->power[] = array($factory->makeModule('Force', [45]),$factory->makeModule('Shot', [99, 50, 2]));// 4 工厂模式更改(依赖)// 由原来对多个外部的依赖变成了对一个 “工厂” 的依赖$factory = new SuperModuleFactory();foreach ($modules as $moduleName => $moduleOptions){$this->power[] = $factory->makeModule($moduleName, $moduleOptions);}}}
如果需要增加能力,还需要更改工厂类 ,噩梦般的感受!。。。
class SuperModuleFactory{public function makeModule($moduleName, $options){switch ($moduleName) {case 'Fight': return new Fight($options[0], $options[1]);case 'Force': return new Force($options[0]);case 'Shot': return new Shot($options[0], $options[1], $options[2]);// case 'more': .......// case 'and more': .......// case 'and more': .......// case 'oh no! its too many!': .......}}}
其实灵感就差一步!你可能会想到更为灵活的办法!
对,下一步就是我们今天的主要配角 —— DI (依赖注入)
由于对超能力模组的需求不断增大,我们需要集合整个世界的高智商人才,一起解决问题,不应该仅仅只有几个工厂垄断负责。
不过高智商人才们都非常自负,认为自己的想法是对的,创造出的超能力模组没有统一的接口,自然而然无法被正常使用。这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口
定义 规范/契约/接口
interface SuperModuleInterface{/*** 超能力激活方法** 任何一个超能力都得有该方法,并拥有一个参数*@param array $target 针对目标,可以是一个或多个,自己或他人*/public function activate(array $target);}
实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,即接口
可以被随时替换却不会产生影响的接口
只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),
而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
依赖注入案例(手动创建并注入):
// 超能力模组$superModule = new XPower;// 初始化一个超人,并注入一个超能力模组依赖 (手动注入)$superMan = new Superman($superModule);
低效率产出(手动)怎么行? 需要自动化!
这种更为高级的工厂,就是工厂模式的升华 ——IoC 容器
class main{/*** main constructor.* 创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。*/public function __construct(){// 创建一个容器$container = new Container;/*** 通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)* 作为生产一个类的实例的 脚本 ,* 只有在真正的 生产(make) 操作被调用执行时,才会触发。*/// 向 容器 添加 超人的生产脚本// 缺点,手动提供超人的模组参数 (更高级的Ioc容器可以自动化处理 - 反射实现)$container->bind('superman', function ($container, $moduleName){// moduleName 参数return new Superman($container->make($moduleName));});// 向 容器 添加 x射线能力的生产脚本$container->bind('xpower', function ($container){return new XPower;});// 向 容器 添加 炸弹能力的生产脚本$container->bind('ultrabomb', function ($container){return new UltraBomb;});// 开始启动生产$superman1 = $container->make('superman', 'xpower');$superman2 = $container->make('superman', 'ultrabomb');$superman3 = $container->make('superman', 'xpower');// ...随意添加}}
自动注入参数 - 反射
手动提供超人所需要的模组参数,但真正的 IoC
容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。
Laravel框架的服务容器正是这么做的。
这种自动搜寻依赖需求的功能,是通过反射(Reflection)实现的
3. Laravel 核心 - IoC 容器
功能模块
比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request(请求)以及 Response(响应)等,
都是与核心无关的类模块提供
这些类从注册到实例化,都是 Laravel 的服务容器负责的
服务提供者
一个类要被容器所能够提取,必须要先注册至这个容器
一个类需要绑定、注册至容器中,才能被“制造”
例如
需要某个服务,就得先注册、绑定这个服务到容器,
那么提供服务并绑定服务至容器的东西,就是服务提供者(Service Provider)。
服务提供者
主要分为两个部分,register(注册) 和 boot(引导、初始化)
class AppServiceProvider extends ServiceProvider{/*** Bootstrap any application services.* 引导、初始化** @return void*/public function boot(){//}/*** Register any application services.* 向容器注册"脚本"* 注册部分如果存在对未知事物的依赖,写在boot中** @return void*/public function register(){//}}
route静态访问
route类 get,post,any 等都不是静态(static)方法,
却能以静态方法访问
# vendor/laravel/framework/src/Illuminate/Routing/Router.php/*** Register a new GET route with the router.** @param string $uri* @param \Closure|array|string|null $action* @return \Illuminate\Routing\Route*/public function get($uri, $action = null){return $this->addRoute(['GET', 'HEAD'], $uri, $action);}
Route::get('/', function() {// bla bla bla...});
使用了门面(Facade)
模拟一个类,提供一个静态魔术方法__callStatic,
并将该静态方法映射到真正的方法上。
Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias()
函数创造的别名而已,这个类被定义在文件
vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php
<?php namespace Illuminate\Support\Facades;/*** @see \Illuminate\Routing\Router*/class Route extends Facade {/*** Get the registered name of the component.** @return string*/protected static function getFacadeAccessor(){return 'router';}}
这个类继承了一个叫做 Facade 的类
getFacadeAccessor 方法返回了一个 route,这是什么意思呢?
这个值被一个 ServiceProvider 注册了真正的路由类 ????