Laravel 服务提供者实例教程 —— 创建 Service Provider 测试实例

从某种意义上说,服务提供者有点类似HTTP控制器,HTTP控制器用于为相关路由注册提供统一管理,而服务提供者用于为相关服务容器提供统一绑定场所,此外服务提供者还可以做一些初始化启动操作。Laravel的每个核心组件都对应一个服务提供者,可以这么说,服务提供者是Laravel的心脏,是Laravel的核心,核心组件类在这里完成注册、初始化以供后续调用。

既然这么重要,那么如何在自己的Laravel应用中定义和使用服务提供者呢?

1、定义服务类

有了上一节有关服务容器的讲述,理解起服务提供者来很简单。我们这里先定义一个绑定到容器的测试TestService,为了对类的定义加以约束,我们同时还定义一个契约接口TestContract

定义TestContract如下:

<?php

namespace App\Contracts;

interface TestContract
{
    public function callMe($controller);
}

定义TestService如下:

<?php

namespace App\Services;

use App\Contracts\TestContract;

class TestService implements TestContract
{
    public function callMe($controller)
    {
        dd('Call Me From TestServiceProvider In '.$controller);
    }
}

2、创建服务提供者

接下来我们定义一个服务提供者TestServiceProvider用于注册该类到容器。创建服务提供者可以使用如下Artisan命令:

php artisan make:provider TestServiceProvider

该命令会在app/Providers目录下生成一个TestServiceProvider.php文件,我们编辑该文件内容如下:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\TestService;

class TestServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     * @author LaravelAcademy.org
     */
    public function register()
    {
        //使用singleton绑定单例
        $this->app->singleton('test',function(){
            return new TestService();
        });

        //使用bind绑定实例到接口以便依赖注入
        $this->app->bind('App\Contracts\TestContract',function(){
            return new TestService();
        });
    }
}

可以看到我们使用了两种绑定方法,更多绑定方法参考服务容器文档

3、注册服务提供者

定义完服务提供者类后,接下来我们需要将该服务提供者注册到应用中,很简单,只需将该类追加到配置文件config/app.phpproviders数组中即可:

'providers' => [

    //其他服务提供者

    App\Providers\TestServiceProvider::class,
],

4、测试服务提供者

这样我们就可以在应用中使用该服务提供者了,为了测试该服务提供者我们首先使用Artisan命令创建一个资源控制器TestController

php artisan make:controller TestController

然后在路由配置文件routes.php中定义路由:

Route::resource('test','TestController');

最后去TestController中编写测试代码:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

use App;
use App\Contracts\TestContract;

class TestController extends Controller
{
    //依赖注入
    public function __construct(TestContract $test){
        $this->test = $test;
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     * @author LaravelAcademy.org
     */
    public function index()
    {
        // $test = App::make('test');
        // $test->callMe('TestController');
        $this->test->callMe('TestController');
    }

    ...//其他控制器动作
}

然后我们去浏览器中访问http://laravel.app:8000/test,分别测试使用App::make和依赖注入解析绑定类调用callMe方法的输出,结果一样,都是:

"Call Me From TestServiceProvider In TestController"

好了,大功告成,是不是很简单?!

此外,Laravel服务提供者还支持延迟加载,具体可参考服务提供者文档

学院君 has written 614 articles

资深PHP工程师,Laravel学院院长

57 thoughts on “Laravel 服务提供者实例教程 —— 创建 Service Provider 测试实例

  1. says:

    使用 $test = App::make(‘test’); $test->callMe(‘TestController’); 这种方法会报 Class test does not exist 的错误,但是使用$this->test->callMe(‘TestController’);就能正常输出,嗯。。。没懂。

  2. SilenMark says:

    您好 学院君,请问在服务提供者register时如何获取路由参数呢,就是根据不同的路由参数返回不同的对象,比如路由参数为foo,返回对像为new someObj(config(‘foo’))

  3. 无比 says:

    请教一下,学院君!按照例子,我已经成功运行出来。之后我在TestService类里,增加了一个方法lookMe。在接口中没有定义相应的契约,控制器中我直接使用$out = $this->test->lookMe(‘what it is’);为什么也成功运行?

  4. 明月有色 says:

    为什么特意在app.php注册?TestServiceProvider完全可以独立去实例服务providers数组系统每次运行都要加载,如果这个服务很少用到那太浪费了

  5. 谢昆林 says:

    学院君,我是初学者,我在app目录下建立了TestContract与TestService,命令空间分别问:namespace AppTestContracts、namespace AppTestServices,然后按照你的步骤新建了一个测试的服务提供者:代码如下:namespace AppProviders;use IlluminateSupportServiceProvider;use AppServicesTestService;class TestServiceProvider extends ServiceProvider{ public function boot(){ } public function register() { $this->app->bind(‘AppContractsTestContract’,function(){ return new TestService(); }); }}然后在app.php中注册:AppProvidersTestServiceProvider::class,路由中也添加了Route::resource(‘test’,’TestController’);,我的TestController的源码是:namespace AppHttpControllers;use IlluminateHttpRequest;use AppHttpRequests;use AppHttpControllersController;use App;use TestService;use AppTestContractsTestContract;class TestController extends Controller{ public function __construct(TestContract $test){ $this->test = $test; } public function index() { $this->app->callMe(‘TestController’); }}然后我在浏览器端测试的时候一直报错,错误信息是:ReflectionException in Container.php line 794:Class AppTestContractsTestContract does not exist。我觉得的错误应该是 public function __construct(TestContract $test){ $this->test = $test;}这个的参数类型限制的问题,但是我更改了很多次,将类型改为:TestService等都不行(命名空间检查过很多次都没错),学院君能不能帮我看看,一直卡在这里了。十分感谢!

        1. 意念无悔 says:

          运行后依然报Class AppContractsTestContract does not exist,试了public function __construct(AppContractsTestContract $test) 这种方式也没有启作用。

  6. 黄俊辉 says:

    class TestController extends Controller{ //依赖注入 public function __construct(TestContract $test){ $this->test = $test; /****** /在这里有没有必要用 parent::__construct();************/ /****还有当路由访问该方法时 参数 TestController $test是怎样传入的 因为我们访问时并没有 指定参数 */ } /** * Display a listing of the resource. * * @return Response * @author LaravelAcademy.org */ public function index() { // $test = App::make(‘test’); // $test->callMe(‘TestController’); $this->test->callMe(‘TestController’); }

  7. dvwxt says:

    我不通过服务提供者注册 初始化直接在TestController中的index方法中这样写也可以实现依赖注入
    public function index(AppServicesTestService $test) {
    $test->call(‘TestControlelr’);
    }
    也可以实现一个已定义类的依赖注入 为什么还要用这种ServiceProvider方法呢?难道是这种注册方法不仅仅实现简单的依赖注入?

    1. 学院君 says:

      你还没有理解依赖注入 你这就像传个对象变量进去 根本不需要注册 所谓的注册是为了绑定接口到实现以实现解耦 比如我定义一个缓存接口 缓存有多种实现 文件 memcache redis等 这些是具体实现类 那么我要在应用中使用文件缓存 绑定缓存接口到文件实现类 然后在具体代码中注入的是缓存接口 这样以后换成memcache缓存 只需要在服务提供者中将绑定改成memcache实现类即可 业务代码无需任何修改 这就是服务提供者的价值所在

      1. dvwxt says:

        看完豁然开朗了,原来依赖注入如此强大!我看了服务容器里面集中绑定方法,请问绑定单例和实例使用方法及场合,文档说获取方法是$this->app->make(‘test’);或者$this->app[‘test’]; 我如何在TestController中获取呢?

发表评论

标记为*的字段是必填项(邮箱地址不会被公开)

你可以使用这些HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>