- 服务容器篇
- 服务提供者篇
- 作为引导者
- 作为管理者
- 启动提供者
- 框架核心
- 目录结构篇
- 应用架构篇
- 解耦处理器
- 其他处理器
- 框架扩展篇
- 管理类和工厂
- 缓存
- 用户认证
- 容器默认绑定
IoC 容器(控制反转容器),借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图所示: 通过 IoC 容器可以帮助我们更方便地管理类依赖,依赖注入 就是传递的参数是一个对象。
在服务提供者中将实现类绑定到所实现的接口,这项工作可以在服务提供者的 register() 方法中完成。
public function register()
{
$this->app->bind(BillingNotifierInterface::class, function ($app) {
return new EmailBillingNotifier();
});
}
注:
注意到我们在定义绑定关系的时候使用的是匿名函数,这样做的好处是用到该依赖时才会实例化,从而提升了应用的性能。
$this->app->bind(BillerInterface::class, StripeBiller::class);
注:这种方式会在绑定时就会实例化 StripeBiller,性能不及匿名函数。
这里,我们只传了一个字符串进去,而不是一个匿名函数。
这个字符串告诉容器总是使用 StripBiller 类作为 BillerInterface 接口的默认实现类。
服务容器就是个用来注册各种接口与实现绑定的地方。
一旦一个类在容器里注册了以后,就可以很容易地在应用的任何位置解析并调用它。
一旦我们使用了服务容器,切换接口的实现就是一行代码的事儿。举个例子,考虑以下代码:
class UserController extends BaseController{
public function __construct(BillerInterface $biller)
{
$this->biller = $biller;
}
}
当这个控制器被服务容器实例化的时候,引用 EmailBillingNotifier 的 StripeBiller 会被注入到这个控制器中。
现在,如果我们想要换一种通知的实现方式,
比如通过短信发送通知(仿照 EmailBillingNotifier 新建一个 SmsBillingNotifier 类),只需在服务提供者中修改绑定到通知接口的实现类即可,其它任何地方都不用修改:
$this->app->bind(BillingNotifierInterface::class, function ($app) {
return new SmsBillingNotifier();
});
利用这种架构设计,我们的应用可以在各种服务的不同实现方式之间快速切换。只改一行代码就能切换接口实现,真的是很强大。
有时候,你可能想在整个应用生命周期中只实例化某类一次,类似单例模式,可以通过 singleton 方法来注册接口与实现类:
$this->app->singleton(BillingNotifierInterface::class, function ($app) {
return new SmsBillingNotifier();
});
现在,只要服务容器解析过这个账单通知对象实例一次,在剩余的请求生命周期中都会使用同一个实例。
单独使用容器:即使你的项目不是基于 Laravel 框架的,依然可以使用Laravel 的服务容器,只要通过 Composer 安装 illuminate/container 就好了。
Laravel 服务容器中最强大的功能之一就是通过反射来自动解析类的依赖。
$reflection = new ReflectionClass(\App\Services\StripeBiller::class);
dump($reflection->getMethods()); # 获取 StripeBiller 类中的所有方法
dump($reflection->getNamespaceName()); # 获取 StripeBiller 的命名空间
dump($reflection->getProperties()); # 获取 StripeBiller 上的所有属性
在 PHP 中,我们不必显式告诉一个方法需要什么类型的参数。
PHP 混合了强类型和鸭子类型(弱类型)结构。为了说明这点,我们来重写一下 billUser 方法:
public function billUser(User $user)
{
$this->biller->bill($user->getId(), $amount);
}
给方法签名加上了 User 类型约束后,我们现在可以确保所有传入billUser 方法的对象,要么是 User 类的实例,要么是一个继承自 User 类的对象实例。
掌握容器:
想了解更多关于容器的知识?去读源码吧!
容器在底层只有一个类Illuminate\Container\Container,读完了你就会对容器如何工作有更深的理解。
一个接口有多钟实现方式例如:
UserRepositoryInterface 可以有 MySQL 和 Redis 两种实现,并且每一种实现都是 UserRepositoryInterface 的一个实例。
服务提供者篇Laravel 服务提供者主要用来进行注册服务容器绑定(即注册接口及其实现类的绑定)。
一个服务提供者必须至少有一个 register 方法。
你可以在这个方法里将类绑定到容器。
当一个请求进入应用,框架启动时,所有罗列在配置文件里的服务提供者的 register 方法就会被调用。
这在应用请求生命周期很早的阶段就会发生,所以在我们编写业务逻辑代码时,所有的服务都已经准备好了。
register Vs. boot 方法:永远不要在 register 方法里面使用任何服务。该方法只是用来将对象绑定到服务容器的地方。所有关于绑定类的解析、交互都要在 boot 方法(服务提供者的另一个方法)里进行。
作为引导者一些通过 Composer 安装的第三方扩展包也会有服务提供者。
这些第三方扩展包的安装说明里一般都会告诉你要在配置文件 config/app.php 的 providers 数组里注册其提供的服务提供者(如果支持包自动发现,则不必这么做)。
只有注册了对应的服务提供者,才能使用扩展包提供的服务。
延迟加载的服务提供者
不是每一个罗列在配置文件 config/app.php 的 providers 数组里的服务提供者在每次请求时都需要被实例化。
这会对性能有影响,尤其是服务提供者注册的服务在这个请求中根本用不到的情况下。
例如,QueueServiceProvider 注册的服务就不是每次请求都用得到,只有在请求用到队列时才会用到。
在 Laravel 5 中,我们通过一种新的方式来实现延迟加载服务提供者,在需要延迟加载的服务提供者中将属性 $defer 设置为 true,并重写 providers 方法即可
,在这个方法中,我们会以数组方式返回该服务提供者注册的服务容器绑定:
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?