必须写 serviceprovider 的只有三类:全局单例类、接口实现绑定、启动早期初始化逻辑;register() 中只能绑定不能调用服务;boot() 才能安全使用其他服务,但不可再注册新服务;类名、路径、配置项三者必须严格一致。

什么时候必须写 ServiceProvider
不是所有类都需要注册到容器里,只有这三类才需要:需要全局单例的类(比如日志处理器)、要绑定接口实现的类(比如 PaymentInterface → AlipayService)、或者要在框架启动早期执行初始化逻辑(比如监听事件、注册路由前缀)。其他情况直接 new 或用 app() 临时取就行,硬塞进 ServiceProvider 反而拖慢启动速度。
register() 里只能做绑定,不能调用服务
register() 阶段容器还没完全就绪,很多服务(比如 config、db)还不可用。常见错误是这里直接读配置或查数据库:
public function register() { // ❌ 错误:config 尚未加载完成 $driver = config('payment.driver'); // 可能返回 null // ✅ 正确:只做绑定,延迟到 resolve 时再取 $this->app->singleton(PaymentService::class, function ($app) { return new PaymentService($app['config']['payment.driver']); }); }
- 绑定用
singleton()/bind()/instance()即可 - 实际构造逻辑放进闭包里,等第一次
app(PaymentService::class)才执行 - 想提前校验配置?改用
boot(),但注意它不保证所有服务都已注册
boot() 是唯一能安全使用其他服务的地方
boot() 在所有 register() 完成后调用,这时 config、Event、view 等核心服务基本可用。典型用途:
- 发布配置文件:
$this->publishes([__DIR__.'/../config/payment.php' => config_path('payment.php')]); - 监听事件:
Event::listen(OrderShipped::class, SendNotification::class); - 注册视图组件:
Blade::component('components.alert', 'alert'); - 但别在这里注册新服务绑定——已经晚了,
app()查不到
ServiceProvider 类名和文件路径必须匹配
laravel 依赖 PSR-4 自动加载,名字错一个字母就找不到类。比如:
- 类名是
AppProvidersPaymentServiceProvider - 文件就必须放在
app/Providers/PaymentServiceProvider.php - 注册到
config/app.php的providers数组里也得写全名:AppProvidersPaymentServiceProvider::class
否则报错 Class "AppProvidersPaymentServiceProvider" not found,不是配置漏了,就是命名空间或路径对不上。
最常被忽略的是:在 boot() 里调用尚未注册的服务(比如自定义的 CacheManager),表面没报错,但运行时才崩——因为 Laravel 不校验依赖顺序,只按数组顺序执行 register()。