Service Container

#介绍

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

Let's look at a simple example:

Laravel服务容器是管理类依赖的强力工具, 依赖注入是比较专业的说法,真正意思是将类依赖透过构造器,或setter方法注入。 让我们看一个简单的例子。

<?php

namespace App\Jobs;

use App\User;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling; class PurchasePodcast implements SelfHandling
{
/**
* The mailer implementation.
*/
protected $mailer; /**
* Create a new instance.
*
* @param Mailer $mailer
* @return void
*/
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
} /**
* Purchase a podcast.
*
* @return void
*/
public function handle()
{
//
}
}

In this example, the PurchasePodcast job needs to send e-mails when a podcast is purchased. So, we will inject a service that is able to send e-mails. Since the service is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the mailer when testing our application.

这个例子中, 当播客被购买时,PurchasePodcast 命令处理器需要发送一封电子邮件。 所以,我们将注入一个服务来提供这个能力。当这个服务被注入后,我们就可以轻易地切换到不同的实现。 当测试我们的应用程序时, 我们同样也可以轻易地“模拟”,或者创建一个虚拟的发信服务实现,来帮助我们进行测试。

A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself.

如果要创建一个强大且大型的应用,或者对Laravel的内核做贡献,首先必须对Laravel的服务容器进行深入了解。

 

#绑定

Almost all of your service container bindings will be registered within service providers, so all of these examples will demonstrate using the container in that context. However, there is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed how to build these objects, since it can automatically resolve such "concrete" objects using PHP's reflection services.

Within a service provider, you always have access to the container via the $this->appinstance variable. We can register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:

(这里5.0文档的翻译不准确,让人看得直挠头。 我这样翻译:)几乎所有的服务容器绑定,都发生在服务提供者里面, 所以,所有这些例子都是在上面语境下使用容器时所展现的。然而,没有必要在容器里绑定类,如果它们不依赖任何接口。 容器不需要被告知如何建立这些对象,因为它会使用PHP的反射服务自动解析这个“具体”对象。

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

Notice that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building.

注意到我们的解析器接收一个容器对象作为引数, 我们然后就可以使用这个容器去解析底下的需要绑定的对象。

 

绑定一个单例

The singleton method binds a class or interface into the container that should only be resolved one time, and then that same instance will be returned on subsequent calls into the container:

singleton方法绑定一个类或接口到容器里面,而且只会被解析一次, 之后相同的实例在以后的调用会被返回到容器里。

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

绑定一个已存在的实例

You may also bind an existing object instance into the container using the instance method. The given instance will always be returned on subsequent calls into the container:

你也可以使用 instance 方法,绑定一个已经存在的实例到容器, 接下来将总是返回该实例。

$fooBar = new FooBar(new SomethingElse);

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

#绑定实例到接口

A very powerful feature of the service container is its ability to bind an interface to a given implementation. For example, let's assume we have an EventPusher interface and aRedisEventPusher implementation. Once we have coded our RedisEventPusher implementation of this interface, we can register it with the service container like so:

服务容器一个很强大的特性是他可以给接口绑定一个给定的实现,举例, 让我们假设我们有个EventPusher 接口, 和要给RedisEventPusher 实现, 一旦我们实现了RedisEventPusher, 我们就可以用服务容器来绑定它。

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

This tells the container that it should inject the RedisEventPusher when a class needs an implementation of EventPusher. Now we can type-hint the EventPusher interface in a constructor, or any other location where dependencies are injected by the service container:

这告诉容器,当一个类需要一个EventPusher的实现的时候,它应该注入RedisEventPusher, 现在我们就可以 在类的构造器中类型提示(type-hint)EventPusher接口,或其他容器可以注入依赖的地方。

use App\Contracts\EventPusher;

/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}

#上下文绑定(context binding)

Sometimes you may have two classes that utilize the same interface, but you wish to inject different implementations into each class. For example, when our system receives a new Order, we may want to send an event via PubNub rather than Pusher. Laravel provides a simple, fluent interface for defining this behavior:

有时候,你有两个类需要用到同一个接口,但是你希望为每个类注入不同的接口实现。 例如当我们的系统收到一个新的订单时, 我们需要使用PubNub来代替Pusher发送消息。Laravel提供了一个简单便利的接口来定义以上行为

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
->needs('App\Contracts\EventPusher')
->give('App\Services\PubNubEventPusher');
You may even pass a Closure to the give method: $this->app->when('App\Handlers\Commands\CreateOrderHandler')
->needs('App\Contracts\EventPusher')
->give(function () {
// Resolve dependency...
});

#标签

 

Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report aggregator that receives an array of many differentReport interface implementations. After registering the Report implementations, you can assign them a tag using the tag method:

偶尔你可能需要解析绑定中的某个类别, 例如你正在建设要给汇总报表,它需要接收实现了Report接口的不同实现的数组。 在注册了Report 的这些实现后,你可以用tag方法来给他们赋予一个标签:

$this->app->bind('SpeedReport', function () {
//
}); $this->app->bind('MemoryReport', function () {
//
}); $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

Once the services have been tagged, you may easily resolve them all via the taggedmethod:

一旦这些服务已经被打标签,就可以用tagged方法轻易解析他们

$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports')); //这里ReportAggregator接收的是一个数组
});

#解析

There are several ways to resolve something out of the container. First, you may use the make method, which accepts the name of the class or interface you wish to resolve:

有几种方法可从容器中解析出某些东西, 首先,你可以使用 make方法, 它接受一个你希望解析的类或接口的名字

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

Secondly, you may access the container like an array, since it implements PHP's ArrayAccessinterface:

第二种,你可以像数组一般使用容器,因为它实现了PHP的ArrayAccess接口

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

Lastly, but most importantly, you may simply "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, middleware, and more. In practice, this is how most of your objects are resolved by the container.

The container will automatically inject dependencies for the classes it resolves. For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class:

最后也是最重要的,你可以在一个类的构造器里面只是去”类型提示”那个依赖,容器会自动注入解析后的类,比如, 你可以在应用的控制器里类型提示一个repository实体类,  ,然后实体类会自动被解析然后注入。

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository; class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users; /**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
} /**
* Show the user with the given ID.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}

#容器事件

The service container fires an event each time it resolves an object. You may listen to this event using the resolving method:

服务容器每次发出一个解析对象的事件, 你使用resolving 方法侦听:

$this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
}); $this->app->resolving(function (FooBar $fooBar, $app) {
// Called when container resolves objects of type "FooBar"...
});

As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer.

被解析的对象传到闭包方法中,允许你设置其他属性。

Laravel5.1学习笔记12 系统架构4 服务容器的更多相关文章

  1. Laravel5.1学习笔记i14 系统架构6 Facade

    Facades 介绍  使用 Facades Facade 类参考   #介绍 Facades provide a "static" interface to classes th ...

  2. Laravel5.1学习笔记13 系统架构5 Contract

    Contract 简介 为什么要用 Contract? Contract 参考 如何使用 Contract 简介 Laravel 中的 Contract 是一组定义了框架核心服务的接口.例如,Illu ...

  3. Laravel5.1学习笔记11 系统架构3 服务提供者

    服务提供者 简介 写一个服务提供者 Register注册方法 Boot 方法 注册提供者 缓载提供者 简介 Service providers are the central place of all ...

  4. Laravel5.1学习笔记10 系统架构2 应用程序结构

    应用程序结构 简介 根目录 App 目录 为应用程序设置命名空间 简介 默认的 Laravel 应用程序结构是为了给无论构建大型还是小型应用程序都提供一个良好的开始.当然,你可以依照喜好自由地组织应用 ...

  5. Laravel5.1学习笔记9 系统架构1 请求生命周期 (待修)

    Request Lifecycle Introduction Lifecycle Overview Focus On Service Providers Introduction When using ...

  6. ODI学习笔记2--ODI产品架构

    ODI学习笔记2--ODI产品架构 ODI产品架构: ODI提供了以下几种管理工具:Designer 用于定义数据转换逻辑,这是最常用的开发工具,大部分的开发任务,包括data store的定义,in ...

  7. SQL反模式学习笔记12 存储图片或其他多媒体大文件

    目标:存储图片或其他多媒体大文件 反模式:图片存储在数据库外的文件系统中,数据库表中存储文件的对应的路径和名称. 缺点:     1.文件不支持Delete操作.使用SQL语句删除一条记录时,对应的文 ...

  8. springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定

    springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定 标签: springmvc springmvc学习笔记12-springmvc注解开发之包装类型參数绑定 需求 实现方 ...

  9. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

随机推荐

  1. 轰炸II

    题目背景 本题为轰炸数据加强版 题目描述 一个城市遭到了M次轰炸,每次都炸了一个每条边都与边界平行的矩形 在轰炸后,有N个关键点,指挥官想知道,它们有没有受到过轰炸,如果有,被炸了几次,最后一次是第几 ...

  2. 0213微信ZABBIX报警

    简介 微信作为日常使用最频繁的工具,因此希望将微信接入zabbix报警. 微信企业号 1.申请微信企业号 申请后,请在“我的企业”页面下记录企业号的CorpID 2.添加通讯录 部门添加完成后,根据实 ...

  3. CODEVS——T 2956 排队问题

    http://codevs.cn/problem/2956/  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Descri ...

  4. Ubuntu 16.04下MySQL 5.7.18取消开机启动(解决无法使用Sysvinit(update-rc.d/sysv-rc-conf)脚本关闭)

    首先了解以下运行级别对应工具的变化历史: 1.Ubuntu 6.10及以前版本使用Sysvinit. 2.Ubuntu 14.10及以前版本使用Upstart但是还留着Sysvinit并存. http ...

  5. 9、Java并发性和多线程-线程安全与共享资源

    以下内容转自http://ifeve.com/thread-safety/: 允许被多个线程同时执行的代码称作线程安全的代码.线程安全的代码不包含竞态条件.当多个线程同时更新共享资源时会引发竞态条件. ...

  6. 又通过一道题目,替换字符串 —— 剑指Offer

    https://www.nowcoder.net/practice/4060ac7e3e404ad1a894ef3e17650423?tpId=13&tqId=11155&tPage= ...

  7. RxJava系列之二 变换类操作符具体解释1

    1.回想 上一篇文章我们主要介绍了RxJava , RxJava 的Observables和 RxJava的just操作符.以及RxJava一些经常使用的操作. 没看过的抓紧点我去看吧. 事实上RxJ ...

  8. C#之选择排序

    算法描述 1.假定未排序序列中第一位为数组最小值,通过与后面的数值进行比较,找到未排序序列中最小值,与未排序序列第一位交换位置: 2.重复步骤一,对剩余未排序序列进行比较找出最小值,与未排序序列中第一 ...

  9. 关于HuffmanCoding的简单分析

    1.what's problem we faced? /** *    Q: what's problem we faced? * *    A: Data compression is still ...

  10. poj 1741 楼教主男人八题之中的一个:树分治

    http://poj.org/problem? id=1741 Description Give a tree with n vertices,each edge has a length(posit ...