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. JavaEE最新技术整理--新技术

    JavaEE最新技术整理-----https://blog.csdn.net/qq_21683643/article/details/79747922

  2. 抽象类和接口有什么区别---https://blog.csdn.net/csdn_aiyang/article/details/71171886

    https://blog.csdn.net/csdn_aiyang/article/details/71171886 概念]   抽象类.具体类是相对的,并非绝对的.抽象是一种概念性名词,具体是一种可 ...

  3. HDU5266 LCA 树链剖分LCA 线段树

    HDU5266 LCA Description 给一棵 n 个点的树,Q 个询问 [L,R] : 求点 L , 点 L+1 , 点 L+2 -- 点 R 的 LCA. Input 多组数据. The ...

  4. 二叉堆练习3&【模板】堆

    时间限制: 3 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 给定N(N≤500,000)和N个整数(较有序),将其排序后输出. 输入描述 Inp ...

  5. ngTbale真分页实现排序、搜索等功能

    一. 真分页表格基础 1. 需求:分页,排序,搜索都是需要发API到服务端. 2. JS实现代码: getStorage是localStorage一个工具方法,可以自己写这个方法. API参数如下: ...

  6. logout退出功能是怎么实现的?login登陆功能室怎么实现的

    logout退出功能是怎么实现的?login登陆功能室怎么实现的 login就是登陆成功的时候,在session里面创建好用户对应的数据. logout就是登出的时候,在session里面销毁用户对应 ...

  7. Solidworks如何另存为和打开OBJ文件

    1 点击工具-插件,勾选ScanTo3D(最好后面的启动也勾选上,否则下次启动将不会默认自动启动这个插件,还需要再做一次才能打开或者另存为OBJ)   2 注意打开零件图的方式,你不能直接打开Soli ...

  8. Arduino程序-光敏电阻

    尽管造书去做的.但还是有莫名的成就感 从串口显示出,电压变化, void setup() {   // put your setup code here, to run once:   Serial. ...

  9. 从头认识Spring-1.9 内部类注入Bean

    这一章节我们来讨论一下内部类注入Bean. 1.domain 蛋糕类:(跟前一章节的一样) package com.raylee.my_new_spring.my_new_spring.ch01.to ...

  10. Android 自己定义UI圆角button

    Android实际开发中我们一般须要圆角的button,普通情况下我们能够让美工做出来对应的button图片.然后放上去就可以,另外我们能够在布局文件里直接设置,也能够达到一样的效果. 以下解说在布局 ...