在 laravel 6 中,服务绑定必须在服务提供者的 register() 方法中进行,不能在中间件中 bind 或 singleton,因为容器绑定是启动期行为,中间件属于运行期,此时绑定无效、不持久且违背设计原则。

在 Laravel 6 中,中间件本身不能直接绑定服务到容器,因为中间件是请求处理链中的一环,它的职责是拦截和处理请求,而非配置容器。服务绑定必须在容器初始化完成、依赖解析开始前完成——也就是在服务提供者(Service Provider)的 register() 方法中进行。
为什么不能在中间件里 bind 或 singleton?
中间件实例是在每次请求时由容器解析并创建的,此时容器已进入运行阶段,大部分服务(如路由、请求、session)正在被使用。若在中间件构造函数或 handle() 中尝试调用 $this->app->bind():
- 绑定操作会被忽略或失效(容器可能已锁定绑定阶段);
- 即使成功,也只对当前请求生效,无法保证后续请求复用;
- 违背容器设计原则:绑定是启动期行为,不是运行期逻辑。
正确做法:在服务提供者中注册自定义服务
你需要创建一个自定义服务提供者(如 AppServiceProvider 或专用提供者),并在其 register() 方法中完成绑定:
- 用
bind()绑定无状态轻量类(如格式化器、策略类); - 用
singleton()绑定有状态或资源密集型服务(如 HTTP 客户端、缓存代理、数据库连接封装); - 支持接口绑定,便于后期替换实现(例如
bind(LoggerInterface::class, FileLogger::class))。
示例:
// app/Providers/AppServiceProvider.phppublic function register()
{
$this->app->singleton('user.analyzer', function ($app) {
return new UserActivityAnalyzer($app->make(Cache::class));
});
$this->app->bind(PermissionChecker::class, DatabasePermissionChecker::class);
}
中间件中如何使用这些已绑定的服务?
中间件可通过构造函数自动注入已绑定的服务(前提是该服务能被容器解析):
- 确保绑定已在
register()中完成,且服务类构造函数参数可被容器自动解析(比如不依赖原始值或未绑定的类型); - 不要在构造函数中注入
Request、Response等请求级对象(会报Target [...] is not instantiable); - 可在
handle()中安全使用注入的服务,配合$request执行业务逻辑。
示例:
class CheckUserAccess{
protected $analyzer;
public function __construct(UserActivityAnalyzer $analyzer)
{
$this->analyzer = $analyzer;
}
public function handle($request, Closure $next)
{
if ($this->analyzer->isBlocked($request->user())) {
return response('Access denied', 403);
}
return $next($request);
}
}
常见陷阱与注意事项
- 绑定代码写在
boot()而非register()→ 可能因依赖尚未注册导致解析失败; - 闭包绑定中手动
new类但未通过$app->make()解析其依赖 → 造成依赖注入断裂; - 单元测试中修改了 singleton 实例状态 → 需在测试后调用
app()->forgetInstance(YourClass::class)清理; - 误用
instance()替代singleton()→ 注入的是已初始化对象,失去自动解析能力。











