服务提供者,在laravel里面,其实就是一个工厂类。它最大的作用就是用来进行服务绑定。当我们需要绑定一个或多个服务的时候,可以自定义一个服务提供者,然后把服务绑定的逻辑都放在该类的实现中。在larave里面,要自定一个服务提供者非常容易,只要继承Illuminate\Support\ServiceProvider这个类即可。下面通过一个简单的自定义服务提供者来说明服务提供者的一些要点:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
protected $defer = true; public function boot()
{
//
} public function register()
{
$this->app->singleton('service1', function(){
return 'service1';
});
$this->app->singleton('service2', function(){
return 'service2';
});
$this->app->singleton('service3', function(){
return 'service3';
});
} public function provides()
{
return ['service1','service2','service3'];
}
}

1). 首先,自定义的服务提供者都是放在下面这个目录的:

其实你放在哪都可以,不过得告诉laravel你的服务提供者在哪,laravel才会帮你注册。怎么告诉它,后面还有介绍。

2)在这个举例里面,可以看到有一个register方法,这个方法是ServiceProvider里面定义的。自定义的时候,需要重写它。这个方法就是用来绑定服务的。你可以在这个服务里面,根据需要加入任意数量的服务绑定。前面要介绍过,在服务提供者里面,始终能通过$this->app拿到容器实例,所以上面的举例中,我们直接用这种方式来完成服务绑定。这个方法是怎么完成服务绑定的呢?因为当laravel找到这个服务提供者的类以后,就会初始化这个服务提供者类,得到一个服务提供者的对象,然后调用它的register方法,自然它里面的所有服务绑定代码就都会执行了。

laravel初始化自定义服务提供者的源码是:

public function registerConfiguredProviders()
{
$manifestPath = $this->getCachedServicesPath(); (new ProviderRepository($this, new Filesystem, $manifestPath))
->load($this->config['app.providers']);
}

这个代码是在Illuminate\Foundation\Application的源码里面拿出来的,从中你能看到laravel会把所有的自定义服务提供者都注册进来。这个注册的过程其实就是前面说的实例化服务提供者的类,并调用register方法的过程。

3). 从上一步的源码也能看到,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...
*/ // /*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Xavrsl\Cas\CasServiceProvider::class,
ThirdProviders\CasServer\CasServerProvider::class
],

所以你如果自己写了一个服务提供者,那么只要配置到这里面,laravel就会自动帮你注册它了。

4)除了register方法,服务提供者里面还有一个boot方法,这个boot方法,会在所有的服务提供者都注册完成之后才会执行,所以当你想在服务绑定完成之后,通过容器解析出其它服务,做一些初始化工作的时候,那么就可以这些逻辑写在boot方法里面。因为boot方法执行的时候,所有服务提供者都已经被注册完毕了,所以在boot方法里面能够确保其它服务都能被解析出来。

5)前面说的服务提供者的情况,在laravel应用程序初始化的时候,就会去注册服务提供者,调用register方法。但是还有一种需求,你可能需要在真正用到这个服务提供者绑定的服务的时候,才会去注册这个服务提供者,以减少不必要的注册处理,提高性能。这也是延迟处理的一种方式。那么这种服务提供者该怎么定义呢?

其实最前面的这个举例已经告诉你了,只要定义一个$defer的实例属性,并把这个实例属性设置为true,然后添加一个provides的实例方法即可。这两个成员都是ServiceProvider基类里面定义好的,自定义的时候,只是覆盖而已。

在基类中,$defer的默认值是false,表示这个服务提供者不需要延迟注册。provides方法,只要简单的返回这个服务提供register方法里面,注册的所有服务绑定名称即可。

延迟注册的服务提供者的机制是:

  • 当laravel初始化服务提供者的实例后,如果发现这个服务提供者的$defer属性为true,那么就不会去调用它的register方法
  • 当laravel解析一个服务的时候,如果发现这个服务是由一个延迟服务提供的(它怎么知道这个服务是延迟服务提供的,是provides方法告诉它的),那么就会先把这个延迟服务提供者先注册,再去解析。这个可以看看Illuminate\Foundation\Application的make方法就清楚了:
  • public function make($abstract, array $parameters = [])
    {
    $abstract = $this->getAlias($abstract); if (isset($this->deferredServices[$abstract])) {
    $this->loadDeferredProvider($abstract);
    } return parent::make($abstract, $parameters);
    }

6)还记得容器实例结构上几个带有providers名称的属性数组吧:

在了解以上provider的机制后,这几个数组的作用也就比较清晰了。其中serviceProviders用来存放所有已经注册完毕的服务提供者:

loadedProviders跟serviceProviders的作用类似,只是存储的记录形式不同:

deferredProviders用来存储所有的延迟注册的服务提供者:

跟前面两个不同的是,deferredProviders存储的记录的key值并不是服务提供者的类型名称,而是服务提供者的provides返回数组里面的名称。并且如果一个服务提供者的provides里面返回了多个服务绑定名称的话,那么deferredProviders里面就会存多条记录:

这样是方便根据服务绑定名称,找到对应的服务提供者,并完成注册。当服务的解析的时候,会先完成延迟类型的服务提供者的注册,注册完毕,这个服务绑定名称在deferredProviders对应的那条记录就会删除掉。不过如果一个服务提供者provides了多个服务绑定名称,解析其中一个服务的时候,只移除该名称对应的deferredProviders记录,而不是所有。

7)服务提供者还有一个小问题值的注意,由于php是一门基本语言,在处理请求的时候,都会从入口文件把所有php都执行一遍。为了性能考虑,laravel会在第一次初始化的时候,把所有的服务提供者都缓存到bootstrap/cache/services.php文件里面,所以有时候当你改了一个服务提供者的代码以后,再刷新不一定能看到期望的效果,这有可能就是因为缓存所致。这时把services.php删掉就能看到你要的效果了。

laravel 服务提供者的更多相关文章

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

    从某种意义上说,服务提供者有点类似HTTP控制器,HTTP控制器用于为相关路由注册提供统一管理,而服务提供者用于为相关服务容器提供统一绑定场所,此外服务提供者还可以做一些初始化启动操作.Laravel ...

  2. laravel 服务提供者介绍和使用

    #安装传送门 安装composer,以及通过composer安装laravel #讲解使用 服务提供者这个具体表现都是围绕着依赖注入 在根目录config/app.php的providers中的数组中 ...

  3. laravel服务提供者类说明

    IoC 是将内部设计的类交给系统去控制,但是有些类在初始化的时候,需要制定特定的参数,或者当你需要将实现类绑定到某个接口,这时候就必须对这些依赖进行配置,系统才能正确解析并引用. register 而 ...

  4. Laravel服务/服务提供者/容器/契约和门面

    1.服务是什么? 服务是提供了一些功能的类,比如发送邮件,写日志. 2.Laravel服务提供者是什么? 服务提供者中指明了这个提供者可以提供哪些服务(注册服务),以及服务注册后默认调用一些方法(bo ...

  5. 谈一谈 Laravel 5.5 的 「自动发现」和此刻心情

    看了Taylor Otwell发表的Package Auto-Discovery In Laravel 5.5第一反应是 为啥... 也怪我,在开发之前忘记看5.5的开发代码动态,之前的文章中也提到过 ...

  6. 聊聊 Laravel 5.5 的 「自动发现」

    ThinkSNS是什么? ThinkSNS(简称TS),一款全平台综合性社交系统,目前最新版本为ThinkSNS+.ThinkSNS V4 ThinkSNS[简]. 看了Taylor Otwell发表 ...

  7. laravel框架总结(五) -- 服务提供者(提及契约Contracts)

    首先理解两个概念 1.契约:一组定义了框架核心服务的接口 2.服务提供者:所有 Laravel 应用程序启动的中心所在. 包括你自己的应用程序,以及所有的 Laravel 核心服务,都是通过服务提供者 ...

  8. laravel门面和服务提供者使用

      关于laravel门面和服务提供者使用的一点见解,门面之词,不足之处,还请多多指教. 在laravel中,我们可能需要用到自己添加的类时,可以建立一个文件夹专门存放类文件,也可以使用laravel ...

  9. 简单理解laravel框架中的服务容器,服务提供者以及怎样调用服务

      laravel被称为最优雅的框架,最近正在学习中,对于用惯了thinkphp.ci框架的人来说,服务容器.服务提供者,依赖注入这些概念简直是一脸懵逼.我花了些时间梳理了一下,也不敢确定自己说的是对 ...

随机推荐

  1. Subtree Minimum Query CodeForces - 893F (线段树合并+线段树动态开点)

    题目链接:https://cn.vjudge.net/problem/CodeForces-893F 题目大意:给你n个点,每一个点有权值,然后这n个点会构成一棵树,边权为1.然后有q次询问,每一次询 ...

  2. 关于Oracle数据库后台进程

    为了最大限度地提高性能并适应许多用户,多进程Oracle数据库系统使用后台进程.后台进程将合并功能,否则这些功能将由运行于每个用户进程的多个数据库程序处理.后台进程异步执行I / O并监视其他Orac ...

  3. 20145215《网络对抗》Exp4 恶意代码分析

    20145215<网络对抗>Exp4 恶意代码分析 基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用 ...

  4. Elasticsearch 5.4.3实战--Java API调用:索引mapping创建

    因为项目开发使用的是Java语言, 项目的开发架构是Spring MVC+ maven的jar包管理,  所以今天重点说说ES 5.4.3 的Java API的源码实战 1. pom.xml文件增加依 ...

  5. filter、map、sorted和reduce函数

    内置函数——filter.map和reduce filter filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter ...

  6. 设计模式C++学习笔记之十(Builder建造者模式)

      建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.一段晦涩难懂的文字,实现创建不同表示的方法就是给创建的过程传入创建的参数.详细的还是看代码吧. 10.1.解释 ...

  7. vc++基础班[26]---进程的相关操作

      ①.ShellExecute 进行进程的创建,属于 shell api: //int iRet = (int)ShellExecute(m_hWnd, _T("open"), ...

  8. SSH远程联机Linux服务器简易安全设定

    分别可以由底下这三方面来进行: 1.服务器软件本身的设定强化:/etc/ssh/sshd_config 2.TCP wrapper 的使用:/etc/hosts.allow, /etc/hosts.d ...

  9. Content-Type的几种常用数据编码格式

    Content-Type,内容类型,一般是指网页中存在的Content-Type,ContentType属性指定请求和响应的HTTP内容类型.如果未指定 ContentType,默认为text/htm ...

  10. xclip for windows

    下载源码和可执行文件 xclip.7z // The MIT License (MIT) // Copyright (c) 2014 Rapptz // Permission is hereby gr ...