应让 laravel 服务容器全程接管对象创建与依赖解析,禁用 new;通过构造函数注入、接口绑定、app()/resolve() 动态获取等方式确保依赖自动注入、可测试、可替换。

避免手动 new 实例化对象,核心是让 Laravel 服务容器全程接管对象的创建与依赖解析。只要绕过容器,类型提示失效、Mock 失效、连接泄漏、状态错乱等问题就会接踵而至。
用构造函数注入替代 new
这是最直接、最推荐的方式。Laravel 在通过路由访问控制器、或调用 app(SomeClass::class) 时,会自动读取构造函数的类型提示,并递归从容器中解析所有依赖。
- ✅ 正确写法:在控制器或服务类中声明类型提示,不写
new - ❌ 错误写法:
$service = new OrderService();—— 容器完全不知情,依赖未注入,也无法被测试替换 - 注意:仅限由容器创建的实例才享受自动注入,比如事件监听器、Artisan 命令、队列任务等,也必须通过
app()或依赖注入获取,不能手写new
绑定接口到实现,再靠类型提示触发解析
光有注入还不行,容器得知道“当你需要 LoggerInterface 时,该给哪个具体类”。这一步在服务提供者的 register() 方法里完成:
-
$this->app->bind(LoggerInterface::class, MonologLogger::class);→ 每次需要都新建一个 -
$this->app->singleton(CacheContract::class, RedisCache::class);→ 全局复用同一个实例(适合连接类) - 绑定后,任何地方只要写
LoggerInterface $logger,容器就能自动提供对应实例,无需new
按需用 app() 或 resolve() 获取,而非 new
某些场景下确实需要在方法内动态取服务(比如条件性使用),这时应调用容器 API,而不是手造对象:
-
$pdf = app(PdfGenerator::class);—— 安全,容器负责构造和注入其依赖 -
$pdf = resolve(PdfGenerator::class);—— 效果相同,语义更明确(当前容器实例去解析) - ⚠️ 切勿:
new PdfGenerator(new Filesystem(), new Config())—— 手动传参耦合严重,且无法被 Mock 或切换实现
特殊类(如验证规则、事件监听器)要适配容器机制
这些类不是由路由自动触发的,容易误用 new。必须主动接入容器流程:
- 自定义验证规则不要在构造函数写依赖,改用
__invoke()+Validator::extend(),内部用app()->make()取服务 - 事件监听器若需依赖,应在
handle()方法参数中类型提示,或在构造函数注入(前提是它本身由容器解析,即注册在EventServiceProvider中) - Artisan 命令同理:依赖写在
__construct(),并确保命令类由php artisan命令启动,而非new实例化











