跳转到主要内容
Laravel 启动时注册的 Service Provider,即使在请求中一次都没用到,也会每次都被加载。延迟 Service Provider(Deferred Service Provider)可以解决这一问题,让服务在真正被使用之前不加载。
本页假定你已阅读包开发基础。建议在理解 Service Provider 的基本机制后再阅读本页。

为什么需要延迟 Provider

普通的 Service Provider 每个请求都会调用 register()boot()。像邮件、队列、缓存等并非所有页面都使用的功能,每次都被初始化是浪费。
// 每个请求都会调用该 Provider 的 register()
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // 每次都要绑定沉重的报表服务
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
将该 Provider 改为延迟后,只有在实际生成报表的请求中才会初始化。

DeferrableProvider 接口

Illuminate\Contracts\Support\DeferrableProvider 是仅有 provides() 方法的简单接口。
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
只要实现这个接口,Provider 就会进入延迟模式。

基本实现

延迟 Provider 的实现分 3 步。
1

implements DeferrableProvider

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    // ...
}
2

在 register() 中绑定服务

与普通 Provider 一样,在 register() 中写入绑定。
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, function ($app) {
        return new ReportGenerator(
            $app->make(PdfRenderer::class),
            $app->make(ChartRenderer::class),
            $app->make(DataExporter::class),
        );
    });

    $this->app->singleton(ReportRepository::class, function ($app) {
        return new ReportRepository($app->make('db'));
    });
}
3

在 provides() 中返回已注册的服务

provides() 必须返回 register()所有已绑定的服务。Laravel 会依据该列表判定「请求哪个服务时该加载这个 Provider」。
public function provides(): array
{
    return [
        ReportGenerator::class,
        ReportRepository::class,
    ];
}

Service Manifest 的机制

Laravel 会在启动时生成清单文件 bootstrap/cache/services.php。该文件保存了延迟 Provider 提供的服务列表。
// bootstrap/cache/services.php 示例(自动生成)
return [
    'providers' => [
        // 与 bootstrap/providers.php 相同的列表
    ],
    'eager' => [
        // 立即加载的 Provider
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // 服务名 => Provider 类 的映射
        'App\Services\ReportGenerator' => ReportServiceProvider::class,
        'App\Repositories\ReportRepository' => ReportServiceProvider::class,
        'cache'     => Illuminate\Cache\CacheServiceProvider::class,
        'cache.store' => Illuminate\Cache\CacheServiceProvider::class,
        'queue'     => Illuminate\Queue\QueueServiceProvider::class,
    ],
    'when' => [],
];
有了这份清单,Laravel 无需加载文件就能知道「哪个 Provider 提供了哪个服务」。实际的 Provider 只有在对应服务首次被解析时才会加载。
添加或修改 Provider 后,请重新生成清单。
php artisan optimize:clear
# 或
php artisan clear-compiled

内部动作流程


provides() 方法的重要性

如果 provides()遗漏某个服务,该服务将永远无法解析。
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // 追加的绑定
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← 以字符串键绑定的服务也不要遗漏
    ];
}
使用 $bindings / $singletons 属性时也一样,需要在 provides() 中列出。
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // 返回 $singletons 中列出的全部键
        return [
            AnalyticsClient::class,
        ];
    }
}

延迟 Provider 的限制

延迟 Provider 适用于仅进行容器绑定注册的 Provider。如果 boot() 中包含以下事项,就不能延迟。
限制原因
路由注册路由在应用启动时解析,延迟无法生效
全局中间件注册需在处理请求之前注册
事件监听器注册(常需生效者)必须在事件触发之前注册
添加 Blade 指令需在视图编译前注册
延迟 Provider 可以写 boot() 方法,但其中的代码在服务被解析前不会执行。如果把路由、中间件之类「总是需要的处理」写在 boot() 中,会得到意料之外的行为。

when() 方法 — 通过事件触发注册

使用 when() 方法可以在特定事件触发时注册 Provider。这适用于仅在特定上下文中需要的 Provider(如 Job 处理相关的 Provider)。
class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, fn () => new ReportGenerator());
    }

    public function provides(): array
    {
        return [ReportGenerator::class];
    }

    /**
     * 不仅在服务绑定解析时加载 Provider,
     * 在指定事件触发时也会加载。
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
一旦 when() 中返回的事件触发,即使服务没有被直接解析,Provider 也会加载。

在包开发中的应用

作为第三方包分发时,延迟 Provider 也能为用户应用的性能做贡献。

推荐模式

namespace Acme\Analytics;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/analytics.php', 'analytics');

        $this->app->singleton(AnalyticsManager::class, function ($app) {
            return new AnalyticsManager($app->make('config')->get('analytics'));
        });

        $this->app->singleton('analytics', fn ($app) => $app->make(AnalyticsManager::class));
    }

    public function boot(): void
    {
        // boot() 只用于发布注册
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
mergeConfigFrom() 会在内部检查配置是否已缓存,因此即使在延迟 Provider 的 register() 中调用也是安全的。但如果配置已被缓存,则不做任何处理。

runningInConsole() 分离 Artisan 命令注册

命令注册只在 Artisan 启动时才需要,所以用 runningInConsole() 做条件判断。不过,如果要延迟一个提供命令的 Provider,请将命令类也加入 provides(),或专门准备一个只提供命令的 Provider。
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}

Laravel 核心中的应用示例

Laravel 核心的很多 Provider 都是延迟 Provider。这是为了避免每个请求都立即加载所有服务。
Provider提供的服务
CacheServiceProvidercache, cache.store, RateLimiter
QueueServiceProviderqueue, queue.worker, queue.failer
MailServiceProvidermail.manager, mailer
RedisServiceProviderredis, redis.connection
HashServiceProviderhash, hash.driver
ValidationServiceProvidervalidator, validation.presence
TranslationServiceProvidertranslator
BroadcastServiceProviderBroadcast
在只有 API 端点的应用中,MailServiceProviderBroadcastServiceProvider 可能在请求过程中一次都不会被加载。

是否应该延迟的判断标准

  • 并非所有请求都使用的服务(邮件、报表、外部 API 客户端等)
  • 初始化需要外部连接或读取文件的服务
  • 拥有庞大对象图的服务
  • 只在 CLI 中使用的命令 Provider
  • 注册路由的 Provider(如 loadRoutesFrom 等)
  • 注册常时生效的中间件或异常处理器的 Provider
  • 注册 Eloquent 全局 Scope 或 Observer 的 Provider
  • 大多数请求都会用到的轻量服务(延迟带来的开销可能更大)

相关页面

包开发基础

以 Service Provider 为核心讲解 Laravel 包的开发方法。

包的版本兼容性管理

应对 Laravel 与 PHP 主版本升级的包维护策略。
最后修改于 2026年7月13日