您当前的位置: 首页 >  ar

知其黑、受其白

暂无认证

  • 5浏览

    0关注

    1250博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Laravel 从学徒到工匠

知其黑、受其白 发布时间:2021-05-27 12:10:26 ,浏览量:5

Laravel 从学徒到工匠
  • 服务容器篇
  • 服务提供者篇
    • 作为引导者
    • 作为管理者
    • 启动提供者
    • 框架核心
  • 目录结构篇
  • 应用架构篇
    • 解耦处理器
    • 其他处理器
  • 框架扩展篇
    • 管理类和工厂
    • 缓存
    • 用户认证
    • 容器默认绑定

服务容器篇

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 方法即可,在这个方法中,我们会以数组方式返回该服务提供者注册的服务容器绑定:

            
关注
打赏
1665558895
查看更多评论
0.2017s