正文
Laravel 服务提供者指南
如果你使用过 Laravel 框架的话,那么,你不可能没听说过服务容器和服务提供者。事实上,它们是 Lavavel 框架核心,它们完成 Larvel 应用中服务启动的艰巨任务。
在这篇文章中,我们将简单介绍「服务容器」,同时还会深入讲解服务提供者。本教程还将演示如何在 Laravel 中创建一个自定义的服务提供者。另外,如果你需要在 Laravel 中成功使用服务容器,还需要注册它。那么,让我们开始吧。
实现一个自定义的服务提供者,需要实现两个非常重要的方法:
boot
和
register
方法。关于这两个方法将在教程最后一个小节讨论。
在学习服务提供者之前,简单介绍一下服务容器,服务容器会在服务提供者中被经常使用。
理解服务容器和服务提供者
什么是服务容器
简而言之,
Laravel 服务容器
是一个用于存储绑定组件的盒子,它还会为应用提供所需的服务。
Laravel 文档中描述如下:
Laravel 服务容器是用于管理类的依赖和执行依赖注入的工具 -
Laravel 文档
这样,当我们需要注入一个内置的组件或服务时,可以在构造函数或方法中使用类型提示功能注入,然后在使用时从服务容器中自动解析出所需实例及其依赖!是不是很酷?这个功能可以让我们从手动管理组件中解脱出来,从而降低系统耦合度。
让我们看一个简单实例来加深理解。
<?php
Class SomeClass
{
public function __construct(FooBar $foobarObject)
{
// use $foobarObject object
}
}
如你所见,
SomeClass
需要使用
FooBar
实例。换句话说它需要依赖其它组件。Laravel 实现自动注入需要从服务容器中查找并执行注入适当的依赖。
如果你希望了解 Laravel 是如何知道需要将哪个组件或服务绑定到服务容器中的,答案是通过服务提供者实现的。服务提供者完成将组件绑定到服务容器的工作。在服务提供者内部,这个工作被称之为服务容器绑定,绑定处理由服务提供者完成。
服务提供者实现了服务绑定,绑定处理则由
register
方法完成。
同时,这又会引入一个新的问题:Laravel 是如何知道有哪些服务提供者的呢?这个我们貌似还没有讨论到吧?我到时看到,之前有说 Laravel 会自动的去查找到服务!朋友,你的问题太多了:Laravel 只是一个框架,它不是一个超级英雄,不是么?我们当然需要去明确的告知 Laravel 框架我们有哪些服务提供者。
让我们来瞧瞧
config/app.php
配置文件。你会找到一个用于 Laravel 应用启动过程中被载入的服务提供者配置列表。
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
Laravel\Tinker\TinkerServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
以上就是有关服务容器的基本概念。下一节,我们将焦点聚集到服务提供者这个核心主题上!
什么是服务提供者
如果说服务容器是提供绑定和依赖注入的的工具,那么
服务提供者
则是实现绑定的工具。
让我们先来看一个内容提供的服务提供者服务来理解它的运行原理。打开
vender/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php
文件。
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});
$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
}
这里我们需要将重点集中在
register
方法中,这个方法用于绑定服务到服务容器。如你所见,这里一共执行了三个服务的绑定处理:
cache
、
cache.store
和
memcached.connector
。
然后,当我们需要在 Laravel 中使用
cache
服务是,服务容器会解析出
CacheManager
实例并返回。也就是说我们仅仅是提供了一个可以从
$this->app
访问的对应关系表。
通过服务提供者绑定服务是 Laravel 服务容器绑定服务的正确打开方式。同时通过服务提供者的
register
方法,还有利于理解 Laravel 服务容器是如何管理所有的服务的。我们之前提到过,通过从
config/app.php
配置文件中读取服务提供者配置列表,从将所有服务注册服务容器中。
以上,就是服务提供者和它的故事。下一节,我们会学习如何创建一个服务提供者来实现将自己的服务注册到 Laravel 服务容器。
自定义服务提供者
Laravel 已经内置了一个用于创建服务提供者的
artisan
命令来简化创建流程。进入命令行模式后执行下面命令来创建服务提供者。
php artisan make:provider EnvatoCustomServiceProvider
运行后会在
app/Providers
目录下创建
EnvatoCustomServiceProvider.php
文件。打开该文件看下它的源码。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class EnvatoCustomServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
之前我们有提到服务提供者有两个重要方法:
boot
和
register
方法,在实现自定义服务提供者时大部分都是在处理这两个方法。
register
方法用于执行服务绑定处理。另外在
boot
方法中可以使用所有已绑定的服务。在这个教程的最后一节我们将学习更多有关这两个方法的细节,但在这里我们会先了解些这两个方法的使用示例加深理解。
注册自定义服务提供者
前面我们创建了一个自定义的服务提供者。接下来需要让 Laravel 知道如何让这个服务提供者同其它服务提供者一样在应用启动时被加载到 Laravel 中。
为了完成注册服务提供者的功能,仅需要将类名加入到
config/app.php
配置文件的
providers
节点。
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
Laravel\Tinker\TinkerServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\EnvatoCustomServiceProvider::class,
],
就是如此简单,现在你已经将自定义服务提供者注册到了 Laravel 中。只不过现在这个服务提供者还几乎什么都没有处理。下一节,我们将以实例演示如何使用
register
和
boot
方法。
深入讲解 register 和 boot 方法
起先,我们来深入研究
register
方法加深你对这个方法的理解。打开之前创建的
app/Providers/EnvatoCustomServiceProvider.php
文件,加入如下代码。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Library\Services\DemoOne;
class EnvatoCustomServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
$this->app->bind('App\Library\Services\DemoOne', function ($app) {
return new DemoOne();
});
}
}
这里我们做了两个处理:
-
引入需要使用的
App\Library\Services\DemoOne
服务。
DemoOne
类现在还没有创建,但之后会创建这个类。
-
在
register
方法中,我们使用服务容器的
bind
方法将服务绑定到容器。这样,当需要使用
App\Library\Services\DemoOne
服务而被解析时,就回调用闭包方法,创建实例并返回
App\Library\Services\DemoOne
对象。
现在创建
app/Library/Services/DemoOne.php
文件。
<?php
namespace App\Library\Services;
class DemoOne
{
public function doSomethingUseful()
{
return 'Output from DemoOne';
}
}
然后,在控制器的构造函数中注入依赖。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Library\Services\DemoOne;
class TestController extends Controller
{
public function index(DemoOne $customServiceInstance)
{
echo $customServiceInstance->doSomethingUseful();
}
}
以上便是一个使用绑定的简单方法。事实上,对于这个示例其实并不需要创建一个服务提供者,并实现
register
方法,因为 Laravel 还可以通过 PHP 的方式功能自动解析。
Laravel 文档中对此有一个说明:
如果我们的依赖无需任何接口,则无需将类绑定到容器。容器此时不需要了解创建对象的具体细节,而可以通过反射功能实现自动注入。
换句话说,如果我们需要绑定的服务依赖于其它接口,创建服务提供者则很有必要。接着来看一个实例以加深理解。
首先,创建一个简单的接口
app/Library/Services/Contracts/CustomServiceInterface.php
。
<?php
// app/Library/Services/Contracts/CustomServiceInterface.php
namespace App\Library\Services\Contracts;
Interface CustomServiceInterface
{
public function doSomethingUseful();
}
然后,创建两个基于此接口的具体实现。或者说,创建两个继承此接口的实现类。
一个是定义在
app/Library/Services/DemoOne.php
文件中的
DemoOne
类。
<?php
// app/Library/Services/DemoOne.php
namespace App\Library\Services;
use App\Library\Services\Contracts\CustomServiceInterface;
class DemoOne implements CustomServiceInterface
{
public function doSomethingUseful()
{
return 'Output from DemoOne';
}
}
类似的,还有
app/Library/Services/DemoTwo.php
。