Laravel5.1学习笔记12 系统架构4 服务容器
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->app
instance 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 tagged
method:
一旦这些服务已经被打标签,就可以用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 ArrayAccess
interface:
第二种,你可以像数组一般使用容器,因为它实现了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 服务容器的更多相关文章
- Laravel5.1学习笔记i14 系统架构6 Facade
Facades 介绍 使用 Facades Facade 类参考 #介绍 Facades provide a "static" interface to classes th ...
- Laravel5.1学习笔记13 系统架构5 Contract
Contract 简介 为什么要用 Contract? Contract 参考 如何使用 Contract 简介 Laravel 中的 Contract 是一组定义了框架核心服务的接口.例如,Illu ...
- Laravel5.1学习笔记11 系统架构3 服务提供者
服务提供者 简介 写一个服务提供者 Register注册方法 Boot 方法 注册提供者 缓载提供者 简介 Service providers are the central place of all ...
- Laravel5.1学习笔记10 系统架构2 应用程序结构
应用程序结构 简介 根目录 App 目录 为应用程序设置命名空间 简介 默认的 Laravel 应用程序结构是为了给无论构建大型还是小型应用程序都提供一个良好的开始.当然,你可以依照喜好自由地组织应用 ...
- Laravel5.1学习笔记9 系统架构1 请求生命周期 (待修)
Request Lifecycle Introduction Lifecycle Overview Focus On Service Providers Introduction When using ...
- ODI学习笔记2--ODI产品架构
ODI学习笔记2--ODI产品架构 ODI产品架构: ODI提供了以下几种管理工具:Designer 用于定义数据转换逻辑,这是最常用的开发工具,大部分的开发任务,包括data store的定义,in ...
- SQL反模式学习笔记12 存储图片或其他多媒体大文件
目标:存储图片或其他多媒体大文件 反模式:图片存储在数据库外的文件系统中,数据库表中存储文件的对应的路径和名称. 缺点: 1.文件不支持Delete操作.使用SQL语句删除一条记录时,对应的文 ...
- springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定
springmvc学习笔记(12)-springmvc注解开发之包装类型參数绑定 标签: springmvc springmvc学习笔记12-springmvc注解开发之包装类型參数绑定 需求 实现方 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
随机推荐
- BZOJ 4819 Luogu P3705 [SDOI2017]新生舞会 (最大费用最大流、二分、分数规划)
现在怎么做的题都这么水了.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=4819 (luogu) https://ww ...
- 如何相互转换逗号分隔的字符串和List --https://blog.csdn.net/yywusuoweile/article/details/50315377
如何相互转换逗号分隔的字符串和List ---https://blog.csdn.net/yywusuoweile/article/details/50315377 方法 2: 利用Guava的Joi ...
- MongoDB怎么快速的删除数据库数据?
我的mongodb里有10+数据库.现在需要重置这个环境,得到干净的没有数据的MongoDB.怎么快速安全的删除这些数据库数据呢? 记得首先备份你的数据库mongodump -o bakfolder ...
- [luoguP2983] [USACO10FEB]购买巧克力Chocolate Buying(贪心)
传送门 按价格排序后贪心 ——代码 #include <cstdio> #include <iostream> #include <algorithm> int n ...
- bzoj 1664 (贪心)
[Usaco2006 Open]County Fair Events 参加节日庆祝 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 487 Solved: ...
- Yet another Number Sequence 矩阵快速幂
Let’s define another number sequence, given by the following function: f(0) = a f(1) = b f(n) = f(n ...
- Eureka集群的那些坑
今天遇到一个Eureka集群的一个坑. 问题现场类似是这样的:两台Eureka组成的服务注册中心集群,两台服务提供方server1.server2,两个服务调用方client1.client2. 正常 ...
- CHAPTER 1 Architectural Overview of Oracle Database 11g
Which SGA structures are required, and which are optional? The database buffer cache, log buffer, an ...
- nodejs fs.open
fs.open(path, flags, [mode], [callback(err, fd)])是 POSIX open 函数的封装,与 C 语言标准库中的 fopen 函数类似.它接受两个必选参数 ...
- ssh2项目整合 struts2.1+hibernate3.3+spring3 基于hibernate注解和struts2注解
项目文件夹结构例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveW9uZ3poaWFu/font/5a6L5L2T/fontsize/400/fi ...