Laravel Laravel
  • 前言

    • 发行说明
    • 升级导引
    • 贡献导引
    • API 文档
  • 安装与配置

    • 安装
    • 配置
    • Homestead
    • Valet
  • 教程

    • 初级任务清单
    • 中级任务清单
  • 基本功能

    • 路由
    • 中间件
    • 控制器
    • 请求
    • 响应
    • 视图
    • Blade 模板
  • 系统架构

    • 请求生命周期
    • 应用程序架构
    • 服务提供者
    • 服务容器
    • Contracts
    • Facades
  • 系统服务

    • 用户认证
    • 用户授权
    • Artisan 命令行
    • 交易
    • 缓存
    • 集合
    • Elixir
    • 加密与解密
    • 错误与日志
    • 事件
    • 文件系统与云存储
    • 哈希
    • 辅助函数
    • 本地化
    • 邮件
    • 扩展包开发
    • 分页
    • 队列
    • Redis
    • Session
    • Envoy
    • 任务调度
    • 测试
    • 表单验证
  • 数据库

    • 数据库入门
    • 查询构造器
    • 数据库迁移
    • 数据填充
  • Eloquent ORM

    • 入门
    • 模型关联
    • Eloquent 集合
    • 修改器
    • 序列化
Icon

提示 您正在浏览旧版本的 Laravel 的文档. 请考虑将你的项目升级到 Laravel 11.x.

0 13

服务容器
5.2
8.x 7.x 6.x 5.8 5.7 5.6 5.5 5.4 5.3 5.2 5.1

Laravel 5.2 中文文档 /

未匹配的标注
本文档最新版为 8.x,旧版本可能放弃维护,推荐阅读最新版!

服务容器

  • 简介
  • 绑定
    • 绑定接口至实现
    • 情境绑定
    • 标记
  • 解析
  • 容器事件

简介

Laravel 服务容器是管理类依赖与运行依赖注入的强力工具。依赖注入是个花俏的名词,事实上是指:类的依赖通过构造器或在某些情况下通过「setter」方法「注入」。

让我们来看个简单例子:

<?php

namespace App\Jobs;

use App\User;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling;

class PurchasePodcast implements SelfHandling
{
    /**
     * 邮件寄送器的实现。
     */
    protected $mailer;

    /**
     * 创建一个新实例。
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * 购买一个播客
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

在这例子中,当播客被购买时,PurchasePodcast 任务会需要寄送 e-mails。因此,我们将 注入 能寄送 e-mails 的服务。由于服务被注入,我们能容易地切换成其它实现。当测试应用程序时,我们能轻易地「mock」,或创建假的邮件寄送器实现。

在构建强大的应用程序,以及为 Laravel 核心贡献代码时,须深入理解 Laravel 服务容器。

绑定

几乎所有的服务容器绑定都会注册至 服务提供者 中,所以下方所有的例子将示范在该情境中使用容器。不过,如果它们没有依赖任何的接口,那么就没有将类绑定至容器中的必要。并不需要为容器指定如何建构这些对象,因为它会通过 PHP 的反射服务自动解析「实际」的对象。

在服务提供者中,你总是可以通过 $this->app 实例变量访问容器。我们可以使用 bind 方法注册一个绑定,传递我们希望注册的类或接口名称,并连同返回该类实例的闭包:

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

注意,我们获取到的容器本身作为参数传递给解析器,这样就可以使用容器来解析绑定对象的 次要依赖。

绑定一个单例

singleton 方法绑定一个只会被解析一次的类或接口至容器中,且后面的调用都会从容器中返回相同的实例:

$this->app->singleton('FooBar', function ($app) {
    return new FooBar($app['SomethingElse']);
});

绑定实例

你也可以使用 instance 方法,绑定一个已经存在的对象实例至容器中。后面的调用都会从容器中返回指定的实例:

$fooBar = new FooBar(new SomethingElse);

$this->app->instance('FooBar', $fooBar);

绑定接口至实现

服务容器有个非常强大的特色功能,就是能够将指定的实现绑定至接口。举个例子,让我们假设我们有个 EventPusher 接口及一个 RedisEventPusher 实现。一旦我们编写完该接口的 RedisEventPusher 实现,就可以将它注册至服务容器:

$this->app->bind('App\Contracts\EventPusher', 'App\Services\RedisEventPusher');

这么做会告知容器当有个类需要 EventPusher 的实现时,必须注入 RedisEventPusher。现在我们可以在构造器中对 EventPusher 接口使用类型提示,或任何需要通过服务容器注入依赖的其它位置:

use App\Contracts\EventPusher;

/**
 * 创建一个新的类实例。
 *
 * @param  EventPusher  $pusher
 * @return void
 */
public function __construct(EventPusher $pusher)
{
    $this->pusher = $pusher;
}

情境绑定

有时候,你可能有两个类使用到相同接口,但你希望每个类都能注入不同实现。例如,当系统收到新订单时,我们可能想通过 PubNub 来发送事件,而不是 Pusher。Laravel 提供一个简单流畅的接口来定义此行为:

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
          ->needs('App\Contracts\EventPusher')
          ->give('App\Services\PubNubEventPusher');

你甚至可以传递一个闭包至 give 方法:

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
          ->needs('App\Contracts\EventPusher')
          ->give(function () {
                  // Resolve dependency...
              });

标记

有些时候,可能需要解析某个「分类」下的所有绑定。例如,你正在构建一个能接收多个不同 Report 接口实现数组的报表汇整器。注册完 Report 实现后,可以使用 tag 方法为它们赋予一个标签:

$this->app->bind('SpeedReport', function () {
    //
});

$this->app->bind('MemoryReport', function () {
    //
});

$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

一旦服务被标记之后,你可以通过 tagged 方法将它们全部解析:

$this->app->bind('ReportAggregator', function ($app) {
    return new ReportAggregator($app->tagged('reports'));
});

解析

有几种方式可以从容器中解析一些东西。首先,你可以使用 make 方法,它会接收你希望解析的类或是接口的名称:

$fooBar = $this->app->make('FooBar');

或者,你可以像数组一样从容器中进行访问,因为他实现了 PHP 的 ArrayAccess 接口:

$fooBar = $this->app['FooBar'];

最后,也是最重要的,你可以在类的构造器简单地对依赖使用「类型提示」,类将会从容器中进行解析,包含 控制器、事件侦听器、队列任务、中间件 等等。在实际情形中,这就是为何大部分的对象都是在容器中解析的。

容器会自动为类注入解析出的依赖。举个例子,你可以在控制器的构造器中对应用程序定义的 Repository 进行类型提示。Repository 会自动被解析及注入至类中:

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository;

class UserController extends Controller
{
    /**
     * 用户 Repository 的实例。
     */
    protected $users;

    /**
     * 创建一个新的控制器实例。
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * 显示指定 ID 的用户。
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        //
    }
}

容器事件

每当服务容器解析一个对象时就会触发事件。你可以使用 resolving 方法监听这个事件:

$this->app->resolving(function ($object, $app) {
    // 当容器解析任何类型的对象时会被调用...
});

$this->app->resolving(FooBar::class, function (FooBar $fooBar, $app) {
    // 当容器解析「FooBar」类型的对象时会被调用...
});

如你所见,被解析的对象会被传递至回调中,让你可以在传递前设置任何额外属性到对象上。


上一篇 下一篇

成为Laravel合作伙伴

Laravel Partners是提供一流Laravel开发和咨询服务的精英商店。我们每个合作伙伴都可以帮助您制定一个精美,结构完善的项目.

我们的伙伴
Laravel
亮点
  • Our Team
  • 发布说明
  • 入门
  • 路由
  • Blade 模板
  • 身份验证
  • 用户授权
  • Artisan 控制台
  • 数据库
  • Eloquent ORM
  • 测试
资源
  • Laravel Bootcamp
  • Laracasts
  • Laravel News
  • Laracon
  • Laracon EU
  • Laracon India
  • Jobs
  • Forums
  • Trademark
  • 版本发布时间
  • 包开发
  • 命令行应用
  • TALL stack全栈开发
  • Blade UI Kit
  • 前端资源构建
伙伴
  • WebReinvent
  • Vehikl
  • Tighten
  • 64 Robots
  • Active Logic
  • Byte 5
  • Curotec
  • Cyber-Duck
  • DevSquad
  • Jump24
  • Kirschbaum
生态系统
  • Cashier
  • Dusk
  • Echo
  • Envoyer
  • Forge
  • Horizon
  • Nova
  • Octane
  • Sail
  • Sanctum
  • Scout
  • Spark
  • Telescope
  • Valet
  • Vapor

Laravel是一个具有表达力,优雅语法的Web应用程序框架。我们认为,发展必须是一种令人愉悦的创造力,才能真正实现。Laravel试图通过减轻大多数Web项目中使用的常见任务来减轻开发的痛苦.

Laravel是Taylor Otwell的商标.
Copyright © 2011-2025 Laravel中文网 LLC.

  • Twitter
  • GitHub
  • Discord
Laravel 全栈开发网 推荐使用阿里云 按Ctrl+D试试