laravel-----容器小记
文章来源:http://laravelacademy.org/post/769.html#ipt_kb_toc_769_6
例子:超人拥有超能力
控制反转定义:
由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)
class Superman
{
protected $power;
public function __construct()
{
$this->power = new Fight(9, 100);
// $this->power = new Force(45);
// $this->power = new Shot(99, 50, 2);
/*
$this->power = array(
new Force(45),
new Shot(99, 50, 2)
);
*/
}
}
问题:
耦合度高,“超人” 对 “超能力” 产生依赖。
解决:
不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组
实现控制反转的方法------工厂模式,依赖转移
class Superman
{
protected $power;
public function __construct(array $modules)
{
// 初始化工厂
$factory = new SuperModuleFactory;
// 通过工厂提供的方法制造需要的模块
foreach ($modules as $moduleName => $moduleOptions) {
$this->power[] = $factory->makeModule($moduleName, $moduleOptions);
}
}
}
问题:
耦合度还是高,由原来对多个外部的依赖变成了对一个 “工厂” 的依赖,由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高级的模组加入,我们必须修改工厂类
IoC 容器的重要组成 —— 依赖注入
定下了一个接口 (超能力模组的规范、契约),所有被创造的模组必须遵守该规范,才能被生产
interface SuperModuleInterface { /** * 超能力激活方法 * * 任何一个超能力都得有该方法,并拥有一个参数 *@param array $target 针对目标,可以是一个或多个,自己或他人 */ public function activate(array $target); }
定义具体类超能力实现接口
class XPower implements SuperModuleInterface { public function activate(array $target) { // 这只是个例子。。具体自行脑补 } }
超人类依赖接口
class Superman { protected $module; public function __construct(SuperModuleInterface $module) { $this->module = $module; } }
什么叫做依赖注入?
只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI)
// 超能力模组 $superModule = new XPower; // 初始化一个超人,并注入一个超能力模组依赖 $superMan = new Superman($superModule);
更为先进的工厂 —— IoC 容器
手动的创建了一个超能力模组、手动的创建超人并注入了刚刚创建超能力模组,低效率产出超人是不现实,我们需要自动化 —— 最多一条指令
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; // 向该 超级工厂添加超人的生产脚本 $container->bind('superman', function($container, $moduleName) { return new Superman($container->make($moduleName)); }); // 向该 超级工厂添加超能力模组的生产脚本 $container->bind('xpower', function($container) { return new XPower; }); // 开始启动生产 $superman_1 = $container->make('superman', 'xpower'); $superman_2 = $container->make('superman', 'ultrabomb'); $superman_3 = $container->make('superman', 'xpower');
过程:
通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。
彻底的解除了 超人 与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖,通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发
现在的例子中,还是需要手动提供超人所需要的模组参数。
而IoC 容器通过反射机制实现自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去
具体代码见Illuminate\Container\Container.php中的build方法
public function build($concrete) { // If the concrete type is actually a Closure, we will just execute it and // hand back the results of the functions, which allows functions to be // used as resolvers for more fine-tuned resolution of these objects. if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } $reflector = new ReflectionClass($concrete); // If the type is not instantiable, the developer is attempting to resolve // an abstract type such as an Interface of Abstract Class and there is // no binding registered for the abstractions so we need to bail out. if (! $reflector->isInstantiable()) { return $this->notInstantiable($concrete); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); // If there are no constructors, that means there are no dependencies then // we can just resolve the instances of the objects right away, without // resolving any other types or dependencies out of these containers. if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); // Once we have all the constructor's parameters we can create each of the // dependency instances and then use the reflection instances to make a // new instance of this class, injecting the created dependencies in. $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }
重新审视 Laravel 的核心
Laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request(请求)以及 Response(响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 Laravel 的服务容器负责的
服务提供者
我们需要某个服务,就得先注册、绑定这个服务到容器,提供服务并绑定服务至容器的东西,就是服务提供者。
为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,Laravel 将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的服务提供者。
服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档。register 负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分
门面
Router::func() 中的router类 ,实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的别名而已。
<?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'; } }
//Face基类的__callStatic方法
public static function __callStatic($method, $args)
{
//获取router实例
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
//获取facade背后的根对象
public static function getFacadeRoot()
{
//从容器中解析facade根实例
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
其中继承的基类Face中有一个__callStatic方法,而getFacadeAccessor()返回一个 route,事实上,这个值被一个ServiceProvider(Illuminate\Foundation\Support\Providers\RouterServiceProvider.php)注册过,
这个才是真正的路由类(Illuminate\Routing\Router.php)!
//真正的Router类
use Illuminate\Routing\Router;
//RouteServiceProvider public function __call($method, $parameters) { return call_user_func_array( [$this->app->make(Router::class), $method], $parameters ); }

浙公网安备 33010602011771号