1、简介

Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。依赖注入听上去很花哨,其实质是通过构造函数或者某些情况下通过 set 方法将类依赖注入到类中。

让我们看一个简单的例子:

<?php

namespace App\Http\Controllers;

use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller; class UserController extends Controller
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users; /**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
} /**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}

在本例中,UserController需要从数据源获取用户,所以,我们注入了一个可以获取用户的服务 UserRepository ,其扮演的角色类似使用 Eloquent 从数据库获取用户信息。注入 UserRepository 后,我们可以在其基础上封装其他实现,也可以模拟或者创建一个假的 UserRepository 实现用于测试。

深入理解 Laravel 服务容器对于构建功能强大的大型 Laravel 应用而言至关重要,对于贡献代码到 Laravel 核心也很有帮助。

2、绑定
绑定基础

几乎所有的服务容器绑定都是在服务提供者中完成。因此本章节的演示例子用到的容器都是在服务提供者中绑定。

注:如果一个类没有基于任何接口那么就没有必要将其绑定到容器。容器并不需要被告知如何构建对象,因为它会使用 PHP 的反射服务自动解析出具体的对象。

简单的绑定

在一个服务提供者中,可以通过 $this->app 变量访问容器,然后使用 bind 方法注册一个绑定,该方法需要两个参数,第一个参数是我们想要注册的类名或接口名称,第二个参数是返回类的实例的闭包:

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

注意到我们将容器本身作为解析器的一个参数,然后我们可以使用该容器来解析我们正在构建的对象的子依赖。

绑定一个单例

singleton 方法绑定一个只需要解析一次的类或接口到容器,然后接下来对容器的调用将会返回同一个实例:

$this->app->singleton('FooBar', function ($app) {
return new FooBar($app->make('HttpClient'));
});

绑定实例

你还可以使用 instance 方法绑定一个已存在的对象实例到容器,随后 调用 容器将总是返回给定的实例:

$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);

绑定原始值

你可能有一个接收注入类的类,同时需要注入一个原生的数值比如整型,可以结合上下文轻松注入这个类需要的任何值:

$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
绑定接口到实现

服务容器的一个非常强大的功能是其绑定接口到实现。我们假设有一个 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;
}
上下文绑定

有时侯我们可能有两个类使用同一个接口,但我们希望在每个类中注入不同实现,例如,两个控制器依赖Illuminate\Contracts\Filesystem\Filesystem 接口的不同实现。Laravel 为此定义了简单、平滑的接口:

use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\VideoController;
use App\Http\Controllers\PhotoControllers;
use Illuminate\Contracts\Filesystem\Filesystem; $this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
}); $this->app->when(VideoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
标签

少数情况下,我们需要解析特定分类下的所有绑定,例如,你正在构建一个接收多个不同 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'));
});
3、解析
make方法

有很多方式可以从容器中解析对象,首先,你可以使用 make 方法,该方法接收你想要解析的类名或接口名作为参数:

$fooBar = $this->app->make('HelpSpot\API');

如果你所在的代码位置访问不了$app变量,可以使用辅助函数app

$api = app('HelpSpot\API');
自动注入

最后,也是最常用的,你可以简单的通过在类的构造函数中对依赖进行类型提示来从容器中解析对象,控制器、事件监听器、队列任务、中间件等都是通过这种方式。在实践中,这是大多数对象从容器中解析的方式。

容器会自动为其解析类注入依赖,例如,你可以在控制器的构造函数中为应用定义的仓库进行类型提示,该仓库会自动解析并注入该类:

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository; class UserController extends Controller{
/**
* 用户仓库实例
*/
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)
{
//
}
}
4、容器事件

服务容器在每一次解析对象时都会触发一个事件,可以使用 resolving 方法监听该事件:

$this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
}); $this->app->resolving(HelpSpot\API::class, function ($api, $app) {
// Called when container resolves objects of type "HelpSpot\API"...
});

正如你所看到的,被解析的对象将会传递给回调函数,从而允许你在对象被传递给消费者之前为其设置额外属性。

核心概念 —— 服务容器的更多相关文章

  1. Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)

    服务容器的绑定 bind 绑定 bind 绑定是服务容器最常用的绑定方式,在 上一篇文章中我们讨论过,bind 的绑定有三种: 绑定自身 绑定闭包 绑定接口 今天,我们这篇文章主要从源码上讲解 Ioc ...

  2. Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器解析)

    make解析 服务容器对对象的自动解析是服务容器的核心功能,make 函数.build 函数是实例化对象重要的核心,先大致看一下代码: public function make($abstract) ...

  3. Laravel开发:Laravel核心——Ioc服务容器

    服务容器 在说 Ioc 容器之前,我们需要了解什么是 Ioc 容器. Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具. 在理解这句话之前,我们需要先了解一下服务容器的来龙去脉:  ...

  4. Docker概念学习系列之Docker核心概念之容器container

    不多说,直接上干货! Docker 利用容器来运行应用. 容器是从镜像创建的运行实例. 它可以被启动.开始.停止.删除.每个容器都是相互隔离的.保证安全的平台. 可以把容器看做是一个简易版的 Linu ...

  5. laravel服务容器(IOC控制反转,DI依赖注入),服务提供者,门脸模式

    laravel的核心思想: 服务容器: 容器:就是装东西的,laravel就是一个个的对象 放入:叫绑定 拿出:解析 使用容器的目的:这里面讲到的是IOC控制反转,主要是靠第三方来处理具体依赖关系的解 ...

  6. docker核心概念(镜像、容器、仓库)及基本操作

    概要 docker是一种linux容器技术.容器有效的将由单个操作系统挂管理的资源划分到孤立的组中,以便更好的在组之间平衡有冲突的资源使用需求.可简单理解为一种沙盒 .每个容器内运行一个应用,不同的容 ...

  7. 01 . 容器编排简介及Kubernetes核心概念

    Kubernetes简介 Kubernetes是谷歌严格保密十几年的秘密武器-Borg的一个开源版本,是Docker分布式系统解决方案.2014年由Google公司启动. Kubernetes提供了面 ...

  8. 面向服务的体系结构(SOA)——(1)目标与核心概念

    什么是SOA? 常常听到人们拿OOP和SOA一起来说事,诸如SOA是否可以代替面向对象(OOP)或者两者比哪个更加有优势?直接回答有难度举个例子可能显得答案更容易理解.小孩子问你该认真写作业呢?还是高 ...

  9. 转 Laravel 的核心 —— 服务容器

    具体内容请参考 1.laravel 学习笔记 —— 神奇的服务容器 - 灵感 - 来自生活的馈赠https://www.insp.top/article/learn-laravel-container ...

随机推荐

  1. CodeForces 706D Vasiliy's Multiset (字典树查询+贪心)

    题意:最开始的时候有一个集合,集合里面只有一个元素0,现在有q次操作,操作分为3种: + x: 表示向集合中添加一个元素x - x:表示删除集合中值为x的一个元素 ? x:表示查询集合中与x异或的最大 ...

  2. C#中托管与非托管

    在.net 编程环境中,系统的资源分为托管资源和非托管资源. 对于托管的资源的回收工作,是不需要人工干预回收的,而且你也无法干预他们的回收,所能够做的 只是了解.net CLR如何做这些操作.也就是说 ...

  3. CPU与内存(经典问答)

    原文:http://www.cnblogs.com/xkfz007/archive/2012/10/08/2715163.html 下面是网上看到的一些关于内存和CPU方面的一些很不错的文章. 整理如 ...

  4. 初学Android 二 创建项目以及目录结构

    命令行创建 android create project Usage: android [global options] create project [action options] Global ...

  5. Node.js和mybatis分别实现mysql中like变量模糊查询

    <!-- mybatis --> <where> <if test="varName != '' and varName != null" > ...

  6. mac下批量删除.svn文件

    mac下.svn是隐藏文件,而且即使我们调成可见的,一个一个删也很麻烦.今天正好同事问起来这个命令,于是想可能有些人也需要,于是还是放到博客里吧 命令比较简单,其实就是一条linux命令,打开终端,首 ...

  7. 用Eclipse来开发STM32

    先贴一个官方说明文档:http://www.keil.com/support/man/docs/ecluv/default.htm

  8. 读《C# 和 Java 的比较》有感

    网上的一篇<C# 和 Java 的比较>(或者叫<Java 和 C# 的比较>)写的挺不错的,今天忽然搜索到. 自己刚刚接触C#,也不由自主地随时都拿来和Java做对比,所以就 ...

  9. ShareSDK for iOS 2.9.0已经公布

    ShareSDK for iOS v2.9.0已经公布,本次更新内容包含: 1.修复Facebook获取用户信息报错问题 2.修复Instagram在iPad上显示分享菜单错误问题,须要指定菜单容器. ...

  10. Android腾讯微博开发之随机字符串与签名实现

    Android腾讯微博开发入门之随机字符串与签名实现   直接上代码   1.Utils类,包括签名和随机字符串   import java.util.Random; import javax.cry ...