来自 澳门威尼斯人平台 2020-01-31 05:21 的文章
当前位置: 澳门威尼斯人平台 > 澳门威尼斯人平台 > 正文

Service Provider Interface

本文由码农网 – 任琦磊原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

1、定义服务类

因实际项目需求,近日在开发laravel-database-logger包的时候,发现设置 ServiceProviderdefer属性设置为true时,会导致在register方法中注册的middleware无效。

@(Java)[SPI]

这是一份面向初学者的 Laravel 5.1 中构建 Service Provider 的教程。

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

class ServiceProvider extends IlluminateSupportServiceProvider{     protected $defer = true;          public function register()     {        $this->mergeConfigFrom(            __DIR__ . '/../config/config.php', 'ibrand.dblogger'        );        $this->app->singleton(DbLogger::class, function  {            return new DbLogger();        });        //当 $defer 设置为 true 时,在路由中引用 databaselogger middleware 会报错,提示 databaselogger class not found.        $this->app[IlluminateRoutingRouter::class]->middleware('databaselogger', Middleware::class);    }        public function provides()    {        return [DbLogger::class];    }}

Service Provider Interface

API的一种设计方法,一般用于一些服务提供给第三方实现或者扩展,可以增强框架的扩展或者替换一些组件。

我在自己过去的博客中提到了我喜欢 Laravel 5.1 的架构,尤其是它引入了Service Provider,从而使你模块化的构建应用成为了可能。应用的配置常常可能成为棘手的任务,完全取决于你正在使用的框架,但幸运的是,我们正在使用的 Laravel 让这件事变得相当简单。

定义TestContract如下:

当问题出现的时候就怀疑是因为设置了defer属性设置为true导致的,立刻就修改源码把protected $defer = true;的代码注释掉,结果仍然是提示databaselogger class not found.,说明 Laravel 并没有注册此ServiceProvder

结构

  • Service
    服务,可以理解为一系列的接口
  • Provider
    Service的实现,一般实现指定的Service接口或者继承指定的类
  • Provider registration and access
    用于注册定义的Provider以及获取指定的Provider实现。

图片 1

<?php

接下来就是想如何解决此问题,尝试了下面的方法:

JDBC中的SPI

由于数据库的多种多样,可以向外提供一组接口(即Service),由数据库厂商来实现对应的接口(即Provider),在使用的时候通过配置加载进来,例如,在JDBC中,其中驱动部分是通过Driver接口以及DriverManager来实现的,如下:

// 定义一些必要的接口(Service)
public interface Driver {
    Connection connect(String url, java.util.Properties info)
    throws SQLException;

    ... 
}

// 向外提供注册以及获取实现的接口(Provider register and access)
public class DriverManager {
    public static synchronized void registerDriver(java.sql.Driver driver) {
        ...
    }
}

对于MySQL来说:

// 驱动实现(Provider)
package com.mysql.jdbc;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {

    static {
        try {
            // 在类加载的时候就进行注册
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    ...
}

后续则可以和普通API一样照常使用,极大的提高了扩展性。

所以让我们开始创建一个用于演示的路由(route)。到 app/Http/routes.php中添加下面这条路由:

namespace AppContracts;

1. 验证本身代码是否存在问题

参考

Wiki:

Book:
Effective Java 2nd, item 1

Route::resource('demo', 'DemoController');

interface TestContract
{
    public function callMe($controller);
}
定义TestService如下:

在正常注册的AppServiceProvider中注册自己的ServiceProvider

通过使用 Route::resource,我们就获得了预定义好的 index,show,create,edit,update,store 和 destroy 路由。

<?php

public function register()    {        //        $this->app->register(IbrandDatabaseLoggerServiceProvider::class);    }

为了实现良好的对称性,现在我们可以使用 artisan 命令行工具来为我们创建对应的控制器(controller)。键入如下指令:

namespace AppServices;

注册后结果一切正常。

php artisan make:controller DemoController

use AppContractsTestContract;

2. 研究源码
config/app.phpproviders注册无效,但是在其他ServiceProvider中注册有效,说明是其他问题。

让我们打开创建好的文件,将 index 方法修改为如下内容:

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

通过研究IlluminateFoundationApplication源码找到registerConfiguredProviders方法:

public function index()
{
    return view('demo.index');
}

2、创建服务提供者

Laravel 是在此方法中去读取config/app.php中的providers内容并loadProviderRepository中。

现在让我们继续在 app/Resources/views 目录下创建一个名为 Demo 的文件夹,并在文件夹中创建一个名为 index.blade.php 的视图(view)文件,内容如下:

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

(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath                    ->load($providers->collapse()->toArray;
@extends('layouts.master')

@section('content')
<h1>Demo Page</h1>
@endsection

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

重点在$this->getCachedServicesPath(),通过源码发现 Laravel 是根据bootstrap/cache/services.php文件去决定如何注册ServiceProvider

这个例子中我们正在调用一个我已经在 layouts 文件夹中创建了的 master 页面master.blade.php。如果你的 master 用了另一个名字,那么这里你得替换掉。如果你没有 master 页面,那么就删掉第一行 extends 的全部内容,包括 @sectioin 申明。

<?php

此时想到了为什么之前注释了//protected $defer = true;代码后仍然无效的原因。

假设你已经配置好了你的开发环境并解析了你的域名,那么当你访问路由yourapplication.com/demo,你应该可以看到内容 Demo Page 了。

namespace AppProviders;

所以为了让注释后的//protected $defer = true;代码有效需要执行

好的,那么现在就让我们来创建一个Service Provider。这个Service Provider不会做太多特别有用的事情。它只是用来向你展示如何搭建它。

use IlluminateSupportServiceProvider;
use AppServicesTestService;

php artisan clear-compiled php artisan optimize

让我们在 app 目录下创建一个 Helpers 文件夹。然后在 Helpers 文件夹里,创建一个 Contracts 文件夹。在 Contracts 文件夹里,创建文件RocketShipContract.php 并写入下面的内容:

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

之后问题就解决了,也更加深入理解了 ServiceProvider 的原理。

本文由澳门威尼斯人平台发布于澳门威尼斯人平台,转载请注明出处:Service Provider Interface

关键词: