[Autowired]属性注入失效的根本原因是类未注册为容器服务:只有通过#[AsService]显式标记或配置方式注册的服务,容器才能扫描、解析并绑定依赖,否则属性恒为null。

autowire 不生效,不是容器“坏了”,而是它根本没看到你的类、或看不懂构造函数参数、或被配置覆盖了——先用命令确认事实,再逐层排除。
debug:container 看服务是否存在
这是第一步,也是唯一可信的判断依据。别猜,直接问容器:
- 运行
bin/console debug:container EmailSender(把EmailSender换成你的类名) - 如果提示 “Service not found”,说明类没注册:检查命名空间是否在
App\下、services.yaml的resource是否覆盖到该路径、有没有被exclude规则误删 - 如果显示
Public: no且你在代码里调用了$container->get('App\Service\EmailSender'),就会报ServiceNotFoundException——必须加public: true - 如果显示
autowired: false,说明该服务被显式关掉了自动装配,哪怕它在扫描路径内也无效
检查 services.yaml 的 autowire 是否真正启用
很多项目看着有配置,其实根本没生效。关键三点必须同时满足:
-
_defaults:必须是services:下的直接子级,缩进错一级就失效 -
autowire: true和autoconfigure: true都得写上,缺一不可 - 扫描路径要对,例如
App\:对应resource: '../src/';如果你的类在src/Domain/Notification/但命名空间是Domain\Notification\,那默认不扫——得单独加一条Domain\: { resource: '../src/Domain/**' }
构造函数参数为什么还是 null
autowire 只能填类型提示明确的服务对象,其他一律跳过:
-
string $apiHost、int $timeout这类标量参数,容器完全不处理,必须用bind:或arguments:显式赋值 - 接口有多个实现(比如两个
LoggerInterface),容器无法决定选谁,会直接放弃注入——用bind:指定默认:Psr\Log\LoggerInterface: '@monolog.logger.request' - 类型提示写成了
object、mixed或空,或者类名拼错(如MailService写成MailerService),容器找不到匹配服务,就当没这参数
#[Autowired] 属性注入为啥一直是 null
属性注入比构造函数注入更脆弱,它依赖两个前提都成立:
- 类本身必须是容器服务——要么加
#[AsService],要么在services.yaml里显式定义;光写注解不注册,等于白写 - 属性不能是
private除非你用的是 Symfony 6.2+ 且容器版本匹配;建议统一用private readonly+ 构造注入,更稳 - 如果类被
autoconfigure: false或autowire: false覆盖过,#[Autowired]就不会触发——它不是绕过配置的后门
最常被忽略的点:你以为 autowire 是“全局开关”,其实它是按服务粒度控制的。一个服务失效,不影响另一个;一次配置错误,可能只卡住某个参数。别改一堆东西,先跑一遍 debug:container 和 debug:autowiring,让容器自己告诉你哪断了。


















