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. mongodb基础系列——数据库查询数据返回前台JSP(二)

    上篇博客论述了,数据库查询数据返回前台JSP.博客中主要使用Ajax调用来显示JSON串,来获取其中某一个字段,赋给界面中的某一个控件. 那这篇博客中,我们讲解,把后台List传递JSP展示. Lis ...

  2. mmap和普通文件读写的区别和比较 & mmap的注意点

    参考 http://www.cnblogs.com/huxiao-tee/p/4660352.html 对linux文件系统不了解的朋友,请参阅我之前写的博文<从内核文件系统看文件读写过程> ...

  3. 前端响应式设计中@media等的相关运用

    现在做前端响应式网站特别,响应式成为现在前端设计一个热点,它成为热点的最主要的原因就是,移动端设备屏幕的种类多样,那么如何设置响应式屏幕. /*打印样式*/ @mediaprint{color:red ...

  4. jqGrid 学习

    jqGrid 学习: 一.下载需要的jqGrid包:http://www.trirand.com/blog/?page_id=6 二.下载JQuery UI:http://jqueryui.com/d ...

  5. JS 之如何在插入元素时插在原有元素的前面而不是末尾

    语法: 父级.insertBefore(新元素,被插入的元素):          //在指定的元素前面加入一个新元素 父级.insertBefore(新元素,父级.children[0]); //在 ...

  6. Weka – 分类

    1.      weka简单介绍 1)  weka是新西兰怀卡托大学WEKA小组用JAVA开发的机器学习/数据挖掘开源软件. 2)  相关资源链接 http://sourceforge.net/pro ...

  7. C++ 不支持模版的分离式编译

    1.C++不支持模版的分离式编译,为什么? C++是分别,单独编译,对于每个cpp文件,预编译为编译单元,这个编译单元是自包含文件,编译的时候,不需要其他的文件,编译好了,生成obj文件,然后连接成e ...

  8. BW导航属性设置

    BW中的Attribute(属性)分为Display Att.和Navigation(导航) Att.,这里我就简称Dis. att和Nav. att了,导航属性可以做为变量来查询和做限制 1.首先进 ...

  9. Uva 3767 Dynamic len(set(a[L:R])) 树套树

    Dynamic len(set(a[L:R])) Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 https://uva.onlinejudge.org/in ...

  10. cocos2d(3.0)一些基础的东西

    1.创建项目后环境配置: 附加文件夹:加入 $(EngineRoot) $(EngineRoot)cocos $(EngineRoot)cocos\editor-support $(EngineRoo ...