目录php
你的应用程序以及 Laravel 的全部核心服务都是经过服务提供器进行引导(注册),服务提供器是配置你的应用程序的中心。api
Laravel 的 config/app.php 文件中有一个 providers 数组。数组中的内容是应用程序要加载的全部服务提供器类。这其中有许多提供器并不会在每次请求时都被加载,只有当它们提供的服务实际须要时才会加载。这种咱们称之为「延迟」提供器,推迟加载这种提供器会提升应用程序的性能。数组
生成文件:app
php artisan make:provider RiakServiceProvider
运行后会发现生成了app/Providers/RiakServiceProvider.php文件composer
生成文件内容:框架
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { // } /** * Register the application services. * * @return void */ public function register() { // } }
从以上文件能够看出:ide
只是单纯的绑定,不注册任什么时候间的监听器、路由、或者其它任何功能,不然你可能使用到未加载的服务memcached
在你的任何服务提供器方法中,你能够经过 $app 属性来访问服务容器:函数
传递咱们想要注册的类或接口名称再返回类的实例的 Closure :性能
$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
singleton 方法将类或接口绑定到只能解析一次的容器中。绑定的单例被解析后,相同的对象实例会在随后的调用中返回到容器中:
$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($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);
此方法在全部其它服务提供者都注册以后才调用,咱们能够访问因此已经被框架注册的服务,好比咱们想实现某个功能,好比中间件功能,事件监听等,你甚至能够在这里注册一条路由,这样就能够是这条路由生效了
public function boot() { view()->composer('view', function () { // }); }
能够在boot方法使用依赖注入
use Illuminate\Contracts\Routing\ResponseFactory; //使用了类型提示 public function boot(ResponseFactory $response) { $response->macro('caps', function ($value) { // }); }
建立好服务提供者以后须要在config/app.php配置文件中注册,写在providers数组中。
'providers' => [ // 其余服务提供器 App\Providers\ComposerServiceProvider::class, ],
你可使用 make 方法将容器中的类实例解析出来。make 方法接受要解析的类或接口的名称:
$api = $this->app->make('HelpSpot\API');
若是你的代码处于不能访问 $app 变量的位置,你可使用全局的辅助函数 resolve:
$api = resolve('HelpSpot\API');
若是你的某些类的依赖项不能经过容器去解析,那你能够经过将它们做为关联数组传递到 makeWith 方法来注入它们。
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
你能够简单地使用「类型提示」的方式在由容器解析的类的构造函数中添加依赖项,包括 控制器、事件监听器、队列任务、中间件 等。 事实上,这是你的大多数对象也应该由容器解析。
例如,你能够在控制器的构造函数中对应用程序定义的 Repository 使用类型提示。Repository 会被自动解析并注入到类中:
<?php namespace App\Http\Controllers; 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) { // } }
上面谈到了注册方法中的绑定,咱们要指到服务容器有一个强大的功能,就是将接口绑定到给定实现。
例如,若是咱们有一个 EventPusher 接口和一个 RedisEventPusher 实现。编写完接口的 RedisEventPusher 实现后,咱们就能够在服务容器中注册它,像这样:
$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\RedisEventPusher' );
使用也很简单,能够在构造函数或者任何其余经过服务容器注入依赖项的地方使用类型提示注入 EventPusher 接口:
use App\Contracts\EventPusher; /** * 建立一个新的类实例 * * @param EventPusher $pusher * @return void */ public function __construct(EventPusher $pusher) { $this->pusher = $pusher; }
分析以上的代码,当一个类须要实现 EventPusher 时,应该注入 RedisEventPusher,咱们可能在更多的地方注入EventPusher,有一天咱们想换成memcachedEventPusher,再想一想,是否是发现了,咱们没必要去一个个修改每个使用EventPusher的类,咱们只须要在服务提供器这里修改就能够了。
$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\memcachedEventPusher' );
有时候,你可能有两个类使用了相同的接口,但你但愿每一个类都能注入不一样的实现。例如,两个控制器可能须要依赖不一样的 Illuminate\Contracts\Filesystem\Filesystem 契约 实现。 Laravel 提供了一个简单、优雅的接口来定义这个行为:
use Illuminate\Support\Facades\Storage; use App\Http\Controllers\PhotoController; use App\Http\Controllers\VideoController; 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')); });
推迟其注册,提升性能,只有在尝试解析其中的服务时候才会加载服务提供者。
要延迟提供器的加载,请将 defer 属性设置为 true ,并定义 provides 方法。provides 方法应该返回由提供器注册的服务容器绑定:
<?php namespace App\Providers; use Riak\Connection; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { //1.是否延时加载服务提供者 protected $defer = true; public function register() { $this->app->singleton(Connection::class, function ($app) { return new Connection($app['config']['riak']); }); } //2.定义provides方法,返回由提供器注册的服务容器绑定 public function provides() { return [Connection::class]; } }
在给消费者使用前,能够作最后一步监听修改(Container Events)
每当服务容器解析一个对象时触发一个事件。你可使用 resolving 方法监听这个事件:
$this->app->resolving(function ($object, $app) { // 当容器解析任何类型的对象时调用... }); $this->app->resolving(HelpSpot\API::class, function ($api, $app) { // 当容器解析类型为「HelpSpot\API」的对象时调用... });
如你所见,被解析的对象会被传递给回调中,让你在对象被传递出去以前能够在对象上设置任何属性。