laravel服务容器支持构造函数注入、方法注入、模型路由绑定及接口绑定四种依赖注入方式,需确保类已注册或可反射实例化,否则会报错。

直接在控制器构造函数或方法参数里写类型提示,Laravel 服务容器会自动解析并注入对应实例——前提是该类已注册进容器,或能被自动反射实例化。
构造函数注入:最常用、最推荐的方式
这是解耦控制器与具体实现的首选方式,所有依赖在对象创建时就明确、可测试、不可变。
-
UserController的__construct()方法参数中写UserService类型提示,容器会自动 new 一个实例并赋给$this->userService - 必须把依赖声明为
protected或public属性,否则注入后无法在其他方法中使用 - 如果依赖类没绑定接口(比如直接用
UserService而非接口),且该类没有构造参数或参数都能被容器解析,就能“开箱即用”;否则需先在AppServiceProvider::register()中用$this->app->bind()显式绑定 - 多个依赖按顺序注入,参数顺序不能错——
UserService $service, NotificationService $notify和NotificationService $notify, UserService $service是两个完全不同的注入序列,后者会导致类型不匹配错误
方法注入:只在单个动作里需要时才用
适用于仅在某个请求处理方法中临时用到的服务,比如表单验证后发通知、上传后调用第三方 SDK。
- 在控制器方法签名里直接写类型提示,例如
public function store(CreateUserRequest $request, EmailService $email) -
CreateUserRequest是 Laravel 内置支持的自动注入类型,会触发验证逻辑;EmailService则走常规容器解析 - 方法注入的参数必须放在路由参数之后(如果有),否则路由值可能被误当作依赖注入——比如
show($id, EmailService $email)没问题,但show(EmailService $email, $id)会让$id变成 null 或报错 - 注意:方法注入无法在中间件执行前生效,所以不能用于需要提前鉴权或日志记录的场景
模型路由绑定注入:隐式但易踩坑
当你在路由里写了 {user},又在控制器方法参数里写 User $user,Laravel 会自动查库找 id = $user 的模型——但这不是普通依赖注入,而是路由模型绑定(Route Model Binding)。
- 它只对实现了
UrlRoutable接口的 Eloquent 模型生效,基础类型(int、string)或自定义类不会触发查找,只会尝试 new 实例 - 参数名必须和路由占位符一致(
{user}→User $user),否则找不到对应值;但类型提示才是决定是否触发模型查找的关键,不是变量名 - 如果同时有多个模型参数(如
{post}/{comment}),顺序错乱会导致一个参数拿到 ID 值、另一个拿到模型实例,结果不可预测 - 想禁用自动查找?把类型改成
int $user或加默认值User $user = null,就能退化为原始路由参数行为
接口绑定 + 类型提示:真正实现松耦合的关键
光写 UserRepositoryInterface $repo 不会自动工作,必须先告诉容器“这个接口对应哪个实现”。
- 在
AppServiceProvider::register()里加$this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class) - 绑定后,任何地方类型提示
UserRepositoryInterface都会得到EloquentUserRepository实例;换实现只需改这一行,不用动控制器 - 若某处需要不同实现(比如测试用 Mock,或生产用缓存版),可用上下文绑定:
$this->app->when(UserController::class)->needs(UserRepositoryInterface::class)->give(TestUserRepository::class) - 别漏掉
use语句——类型提示里的类名必须能被自动加载,否则容器反射失败,报Class XXX does not exist
最常被忽略的一点:依赖注入不是“写上类型提示就完事”,它依赖容器能否成功解析。当遇到 Target [XXX] is not instantiable,第一反应不该是删掉类型提示,而应检查类是否存在、是否可被 new、是否有未满足的构造参数、接口是否已绑定——这些才是真实卡点。











